Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AsyncTask, Fragments, Views, and Backstacks

I've been using this pattern for a bit. Here is a very contrived example of an AsyncTask + progress indicator:

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();

It works okay with orientation changes, but I've noticed the fragment being non-null and fragment.getView() being null when I pop the fragment off the backstack. That obviously causes this to crash. What approach are you folks using? I can't seem to find a perfect solution online. Important note, this is in a fragment and I call setRetainInstance(true); on that fragment in its onActivityCreated(...).

like image 922
JVillella Avatar asked Oct 17 '14 19:10

JVillella


1 Answers

AsyncTask is an asynchronous task which means you usually do not know when it is completed and when it calls onPostExecute(). I think your problem is here. When you rotate your device your asynctask has not finished yet and your fragment view is destroyed and recreates again very quickly while still asynctask is on the worker thread and when it has done its job your fragment has a view and getView's return value is not null. But if the rotation time takes much you will get null because the AsyncTask has finished but your fragment does not have any view. Now this scenario is exactly happens to your backstack When you push your fragment on to the backstack its view is destroyed(look at the picture) but the fragment itself is not (the fragment returns to the layout from backstack). Now your AsyncTask finishes and call your fragment getView method and it dose not have any view so you will get NPE.

My solution:

First you should use WeakReference<ProgressBar> instead of WeakReference<MyFragment> and in onPostExecute() check whether it is null or not, if it is null do nothing because the default value is invisible if it is not null call setVisibility(View.GONE).

Update:

the fragment is alive but the view has been dealloc'd. Is this possible?

Yes, just look at the figure2 from link. When fragment is active (green box) the fragment added to back stack fragment goes to onPause, onStop onDestroyView. Then when the fragment pops up from back stack it goes right to onCreateView the arrow link with this text:the fragment returns to the layout from backstack.

In order to confirm my answer you can create an AsyncTask and call SystemClock.sleep(30000) inside your doInbackground. Now you can push your fragment in to the backstack and pops it up from backstack with no exception because the asynctask has not finished and when it will call onPostExecute() your fragment already has a view.

Another good thing you can see is setRetainInstance

This can only be used with fragments not in the back stack.

So when you push your fragment into back stack it may destroy completely and you may get new fragment reference when it pops up. But when you use setRetainInstance(true); for configuration changes your fragment is retained across Activity re-creation which means it is the same fragment and onCreate is not called. And this is a very very important thing because if you start your AsyncTask at the onCreate method, when you push your fragment into the back stack and pop it up you may have a new fragment and this means onCreate method runs and another AsyncTask is fired. That means you do not have any control over the number of calling AsyncTasks so watch out for memory leaks!

like image 198
mmlooloo Avatar answered Nov 03 '22 09:11

mmlooloo