AsyncTask,Fragments,Views和Backstacks

时间:2014-10-17 19:48:26

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

我一直在使用这种模式。这是一个非常人为的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();

对于方向更改它可以正常工作,但是当我从后台堆栈中弹出片段时,我注意到片段是非null而fragment.getView()为null。这显然会导致崩溃。你们用什么方法?我似乎无法在线找到完美的解决方案。重要提示,这是一个片段,我在onActivityCreated(...)上对该片段调用setRetainInstance(true);

1 个答案:

答案 0 :(得分:4)

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

我的解决方案:

首先,您应该使用WeakReference<ProgressBar>而不是WeakReference<MyFragment>并在onPostExecute()中检查它是否为空,如果它为null则不执行任何操作,因为如果不是,则默认值不可见null call setVisibility(View.GONE)

更新

  

这个片段还活着,但视图已经被释放了。这是   可能的?

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

为了确认我的回答,您可以在AsyncTask内创建SystemClock.sleep(30000)并致电doInbackground。现在你可以将你的片段推入到backstack并从backstack弹出它,没有异常,因为asynctask还没有完成,当它调用onPostExecute()时你的片段已经有了一个视图。

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

  

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

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