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(...).
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 AsyncTask
s so watch out for
memory leaks!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With