How to Handle Asynctask Onpostexecute When Paused to Avoid Illegalstateexception

How to handle AsyncTask onPostExecute when paused to avoid IllegalStateException

If you need to synchronize your task with the activity lifecycle, I believe that Loaders are exactly what you need. More specifically, you should use AsyncTaskLoader to do the job. So now instead of running an AsyncTask, you launch your loader, then wait for response in a listener. If the activity is paused, you won't get a callback, this part will be managed for you.

There is another way to handle this task: using a fragment which retains its instance. The general idea is that you create a fragment without UI and call setRetainInstance(true). It has a task which is being notified about the activity being available or not. If not, the task's thread suspends until an activity becomes available.

popBackStack in onPostExecute causes IllegalStateException

Like x90 mentioned in the comment above, the easiest solution would be to split your fragments up into separate activities. This will obviously avoid the exception because launching a new activity doesn't require modifying the state of the previous activity. Swapping fragments in/out of the screen like this is probably not what you want to do anyway... as their names suggest, Fragments were designed to serve as "fragments" of the user interface, and not as much as entire screens which make up the user interface. (Of course, I don't mean to suggest that you aren't allowed to use Fragments in this way... I'm just saying that it wasn't a design goal of the Fragment API).

You could also try to implement some sort of cancellation policy in your AsyncTask. That is, if the activity ever begins to go into the background, cancel the task immediately. When the AsyncTask finally finishes, make sure that isCancelled() returns false before performing fragment transactions, etc.

How can I avoid an IllegalStateException if I need to add a Fragment in onNewIntent() or after a run-time change?

I've tried using commitAllowingStateLoss() but I still get the exception because my spinner Fragment is still referencing the old destroyed Activity.

your fragment was recreated after config change, ie. after user have rotated screen - your spinner fragment will be destroyed and recreated, and after onAttach it will reference new activity instance. Also all of this process is done by android on UI thread in single message, so there is no chance that your async operation callback (which should execute also on UI thread) gets executed in the middle.

I assume here you are not creating some local reference to activity inside ProgressFragment.

You could write additional logic that would make sure your commit is called in valid moment. ie. in your activity onStart set some static boolean allowCommit to true, and in onPause set it to false. Also add some static variable, searchWasFinished, and in your async callback check if allowCommit is true if so then immediately remove spinner, if not then only set searchWasFinished to true. Inside your Activity.onStart check if searchWasFinished==true and if so then remove fragment with commit. This is just an idea, probably more logic would have to be put in it.

IlegalStateException when onItemClick after restart

The general problem is losing the context. You are right that keeping a reference to the old Activity is a bad way because the Android system should manage the Activity's lifecycle. But you kept the reference to FragmentManager in FragmentChange class when static FragmentChange instance is created (FragmentChange.java : 46).

So what really happens: you created static instance of FragmentChange. The FragmentChange instance keeps reference to FragmentManager that is linked with MainActivity instance . When you press back, the system calls the MainActivity instance lifecycle callback. It calls onStop() callback that also invokes dispatchStop() in FragmentManager instance where to mStateSaved variable is assigned to true. And after that the MainActivity instance is destroyed. But FragmentChange instance is kept because the application isn't destroyed, just the activity instance . When you return to the app, the new MainActivity instance will be created. But your FragmentChange still keeps the reference to the old FragmentManager instance that is linked to the dead MainActivity instance . So no one will call dispatchCreate(), or dispatchResume(), or any other method to restore the mStateSaved value to false, the old FragmentManager instance isn't linked to the app's life cycle. And when you select Employees once again, the old FragmentManager instance throws IllegalStateException because mStateSaved still has true value.

The general solution is not to create the static reference to Activity, Fragment, View, FragmentManager and so on. In general, to all classes which lifecycle is Android system business. The one of the possible solutions for your case is not to keep a reference to the FragmentManager but to send it as a parameter to onFragmentChange. I provided a code example in the pull request https://github.com/emnrd-ito/ReplaceFragment-OnItemClick/pull/1/files

Best way to Handle FragmentTransaction in AsyncTask.onPostExecute after onDestroy is called

How @Raghunandan suggested, i followed this post and everything worked fine. The big difference is that now i call my ApplicationStarter on onCreate of my Fragment which is set to setRetainInstance(true). The InitialActivity has an instance of ItensFragment. The code in my InitialActivity's onCreate() is:

public class InitialActivity extends SherlockFragmentActivity {
private ItensFragment itensfrag;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inicial);
FragmentManager fm = getSupportFragmentManager();
itensfrag= (ItensFragment) fm.findFragmentByTag("itensfrag");
if (savedInstanceState == null) {
if (itensfrag== null) {
itensfrag= new ItensFragment();
fm.beginTransaction().replace(R.id.simple_fragment, itensfrag, "itensfrag").commit();
}
}
}
}

Android. Run ui operations in onPostExecute when the fragment is paused

You can't update the UI of or launch a fragment from a background activity as the activity is paused (onPause() called). What you did is the right thing.

Why would an AsyncTask's postExecute method not run in the UI thread?

I believe what @hovanessyan is getting at is that it is not safe to assume that the Fragment your AsyncTask was talking to when it launched may not be the same instance as when it finishes executing. Android may pause or even terminate the Fragment while the AsyncTask is running. That would explain why the problem is intermittent. It is only happening to users when their Fragment has been paused or destroyed and recreated.

This issue is discussed in this article. If you read items 2 and 3 in the article you will see how to address it. You are responsible for saving and restoring state as part of your Fragment life cycle. See android documentation of Fragment lifecycle.

Proper way of dismissing DialogFragment while application is in background

DialogFragment has a method called dismissAllowingStateLoss()

How can you handle dismissing a DialogFragment (compatibility lib) upon completion of an AsyncTask

To get around the illegal state exception issue and essentially implement a dismissAllowingStateLoss() can be done using the following.

getFragmentManager().beginTransaction().remove(someDialog).commitAllowingStateLoss();

This should solve the issue without the hacky code. The same can also be applied for show if you have threads communicating through a handler with the UI thread using dialog.show(); Which can cause an illegal state exception as well

getFragmentManager().beginTransaction().add(someDialog).commitAllowingStateLoss();


@joneswah is correct, given the posters question.
If you are using the support library, replace

getFragmentManager()

with

getSupportFragmentManager()


For future Googlers:
@Alex Lockwood raises good and valid concerns with this solution. The solution does solve the error and will work in most cases, but hints that there are issues with the approach in the original question, from a UX perspective.

The Activity should assume that the async task may not complete and that it will not perform onPostExecute(). Whatever UI action (ie, spinner, ideally not a dialog) is started to notify the user of the async operation, should have provisions to stop automatically either on a timeout or by tracking state and checking in onRestore/onResume type lifecycle events to ensure the UI is updated properly. Services may also be worth investigating.



Related Topics



Leave a reply



Submit