【问题标题】:AsyncTask, Fragments, Views, and BackstacksAsyncTask、Fragments、Views 和 Backstacks
【发布时间】:2014-12-13 11:24:07
【问题描述】:

我一直在使用这种模式。这是一个非常人为的 AsyncTask + 进度指示器示例:

new AsyncTask<Void, Void, Void>() {
    WeakReference<MyFragment> weakFragment = new WeakReference<MyFragment>(MyFragment.this);

    @Override
    protected void onPreExecute() {
        Fragment fragment = weakFragment.get();
        if(fragment != null) fragment.getView().findViewById(R.id.progress).setVisibility(View.VISIBLE);        
    }

    @Override
    protected Document doInBackground(Void... params) {
        Fragment fragment = weakFragment.get();
        if(fragment == null) return null;

        // Do something ...
    }

    @Override
    protected void onPostExecute() {                
        Fragment fragment = weakFragment.get();
        if(fragment != null) fragment.getView().findViewById(R.id.progress).setVisibility(View.GONE);
    }
}.execute();

它适用于方向更改,但是当我从后台堆栈中弹出片段时,我注意到片段是非空的并且 fragment.getView() 是空的。这显然会导致崩溃。你们用的是什么方法?我似乎无法在网上找到完美的解决方案。重要说明,这是在一个片段中,我在其 onActivityCreated(...) 中对该片段调用 setRetainInstance(true);

【问题讨论】:

  • 使用消息总线或本地广播接收器。
  • 进度条? :|你不觉得这有点多吗?

标签: android android-fragments android-asynctask progress-bar


【解决方案1】:

AsyncTask 是一个异步任务,这意味着您通常不知道它何时完成以及何时调用onPostExecute()。我想你的问题就在这里。当您旋转您的设备时,您的 asynctask 尚未完成,并且您的片段视图被销毁并很快重新创建,而 asynctask 仍在工作线程上,当它完成工作时,您的片段有一个视图和getView 的返回值不是null。但是如果旋转时间很长,你会得到null,因为AsyncTask 已经完成,但是你的片段没有任何视图。现在这种情况恰好发生在您的 backstack 上。当您将片段推送到 backstack 时,它的视图被破坏(look at the picture)但片段本身不是(片段从 backstack 返回到布局)。现在你的AsyncTask 完成并调用你的fragment getView 方法,它没有任何view 所以你会得到NPE

我的解决方案:

首先你应该使用WeakReference&lt;ProgressBar&gt;而不是WeakReference&lt;MyFragment&gt;,并在onPostExecute()中检查它是否为null,如果它为null则什么也不做,因为如果它不是null,则默认值是不可见的调用setVisibility(View.GONE)

更新:

片段还活着,但视图已被释放。这是 可能吗?

是的,只需查看链接中的图 2。当片段处于​​活动状态时(绿色框)the fragment added to back stack 片段转到onPauseonStop onDestroyView。然后,当片段从后堆栈弹出时,它会直接转到onCreateView 带有此文本的箭头链接:the fragment returns to the layout from backstack

为了确认我的回答,您可以创建一个AsyncTask 并在您的doInbackground 中调用SystemClock.sleep(30000)。现在您可以将您的片段推入后台堆栈并从后台堆栈弹出它,因为异步任务尚未完成并且何时调用onPostExecute()您的片段已经有一个视图。

你可以看到的另一件好事是setRetainInstance

这只能用于不在后台堆栈中的片段。

因此,当您将片段推入回栈时,它可能完全销毁,并且您可能在它弹出时获得新的片段引用。但是,当您使用setRetainInstance(true); 进行配置更改时,您的片段在重新创建活动时保留,这意味着它是相同 片段并且不会调用onCreate。这是一件非常非常重要的事情,因为如果您在 onCreate 方法中启动您的 AsyncTask,当您将片段推入后端堆栈并弹出它时,您可能有一个新片段并且这意味着 onCreate 方法运行并且另一个 AsyncTask 被触发。这意味着您无法控制调用AsyncTasks 的次数,因此请注意 内存泄漏!

【讨论】:

  • 对,我知道你来自哪里。这就是为什么我首先使用对片段的弱引用。我认为遇到的行为是片段是活动的,但视图已被释放。这可能吗?也许对此进行更多澄清会有所帮助。
  • 感谢您的帮助@mmlooloo。这可以解决问题,但我仍然不确定在异步任务中处理 fragment 和 fragment.getView() 的最佳实践。
  • 我不明白你的意思,但你的问题不是很复杂,只是在某些情况下你有一个片段并且那个片段没有任何视图,所以调用 getView 会导致 NPE。跨度>
  • 如果我需要访问片段的视图,现在我需要检查片段是否处于活动状态(因为我应该持有弱引用),检查它的 getView() 是否处于活动状态,可能所有它的数据成员也可能导致它们为空。它是否正确?对于一些非常小的东西来说,它似乎太过分了。
  • 是的,但我想告诉你的是:在某些情况下,你的片段是活着的,但没有任何视图。所以只需在 NPE 中调用 getView 结果。为了解决将您的小部件存储在弱参考而不是片段中的问题。如果您注意到,您也不需要任何共享首选项我更新了我的答案的解决方案部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多