【问题标题】:Fragment loses Activity while networking (android)联网时片段丢失活动(android)
【发布时间】:2017-09-28 20:37:31
【问题描述】:

我从 Fragment 发起大部分网络调用,然后使用回调告诉 Fragment 网络任务是成功还是失败,并相应地更新 ui。

在极少数情况下(0.25% 的会话),由于 getActivity() 在我的回调中的代码运行时返回 null,我的程序会因空指针异常而崩溃。我知道我可以对 getActivity() 使用空检查来防止这种情况发生,但是处理这个问题的最佳做法是什么?

null 检查似乎只不过是一个崩溃预防工具,因为程序仍然需要来自网络任务的数据。

代码如下所示:

private void queryServer() {
    // networking task should query server for user id, if successful store it 
    // in user preferences to be accessed by fragment in callback
    new networkingTask(new VolleyCallback() {
        @Override
        public void onSuccess() {
            // code below needs null check on getActivity - but what else?
            mUserId = new UserPreferences(getActivity()).getUserId();
        }

        @Override
        public void onFail() {
            // booooo
        }
    });
}

【问题讨论】:

  • 你应该熟悉片段和活动的生命周期方法。可能发生的是系统正在停止或销毁 Activity/Fragment 对。发生这种情况的原因有很多,例如屏幕方向更改。因为您的处理程序是片段对象上的一个方法,所以在调用返回时您正在使用“死”片段。有几种模式可以处理这个问题。简而言之,您需要让您的处理程序知道当前的片段,您可以通过使用生命周期方法来完成此操作。

标签: android android-fragments android-lifecycle


【解决方案1】:

正如我在上面的评论中所说,可能发生的情况是系统正在停止或销毁 Activity/Fragment 对。发生这种情况的原因有很多,例如屏幕方向更改。因为您的处理程序是片段对象上的方法,所以在调用返回时您正在使用“死”片段。有几种模式可以处理这个问题。简而言之,您需要让您的处理程序知道当前的片段,您可以通过使用生命周期方法来完成此操作。

以下是您可以使用的模式示例。我试图使示例尽可能少。

import android.app.Activity;
import android.app.Fragment;

public class MyFragment extends Fragment {

    // This is static so that it will not go out of scope when the original
    // fragment is destroy.  This allows it to be access from all MyFragment
    // instances.
    static MyResponseProcessor processor = new MyResponseProcessor();

    // This will be the class that handles your network call.  
    public static class MyResponseProcessor {

        // This instance variable is alway a reference to the currently displayed fragment.
        private Fragment activeFragement;

        public void setActiveFragement(Fragment activeFragement) {
            this.activeFragement = activeFragement;
        }

        // This method, which is for demonstration purposes, shows how you would handle a network response.
        public void handleResponse(SomeResponseObject) {
            if (activeFragement != null) {
                // Now you can get the activity
                Activity activity = activeFragement.getActivity();
            } else {
                // Yes it is possible that there is no active fragment.  
                // If the user has stayed on the same screen, then the
                // fragment of interest will likely be re-created, and 
                // this window of time with no fragment will be brief.
                //
                // Note that this null-check is very different than the 
                // null-check you describe.  In your case the reference is
                // guaranteed to be null forever.  In this case, the reference
                // will eventually become non-null.
            }
        }
    }

    @Override
    public void onStart() {
        super.onStart();
        // At this point in the fragment lifecycle, the fragment is both running and is attached to an Activity.
        // Thus "getActivity" calls are safe from this point onward.
        processor.setActiveFragement(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        // At this point in the fragment lifecycle, the fragment has been stopped and is about to lose its connection to the activity.
        // So after this point, calls to "getActivity" are probably not safe.
        // DISCLAIMER - I have not tested this.  You might want to do this in a
        // different method such as "onDestroyView()"
        processor.setActiveFragement(null);
    }
}

【讨论】:

  • 你能解释一下为什么我的引用会保证永远为空吗?两个空检查都依赖于 getActivity...
  • 另一个问题是,如果我的回调代码无法执行,在我的应用程序中的某些情况下,它会导致整个用户操作失败,因为回调只是在另一个网络调用之前停止。
  • 至于为什么您的引用会保证永远为空。因为一旦 Android 系统销毁了你的 Activity/Fragment,Android 很可能会创建一个新的 Activity/Fragment 对来代替原来的位置。作为片段销毁的一部分,与活动的连接是“无线的”。 “getActivity()”的 null 返回值是 Android 告诉您片段不再位于活动中的方式。
  • 您的处理程序似乎只需要该活动来从中获取值(用户ID)。您的活动可以将该值保存在其他地方(在 Singleton 或 SharedPreferences 中)。然后您的处理程序将不再需要访问该活动。
  • SharedPreferences 和 Singletons 依赖于活动的活动。 (1) 单例模式与 Android 无关,因此不需要 Android 上下文。 (2) 如果您选择使用 SharedPreferences,是的,您需要一个上下文,但不,该上下文不必是当前活动的上下文。您总是可以只使用永远不会超出范围的应用程序上下文。这是一篇文章,描述了您应用中的任何类如何随时访问应用上下文:stackoverflow.com/questions/2002288/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-28
  • 1970-01-01
  • 1970-01-01
  • 2021-06-09
  • 1970-01-01
相关资源
最近更新 更多