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, Fragment
s 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 Fragment
s 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
Activity.Finish() Called But Activity Stays Loaded in Memory
In HTML5, How to Keep an Android Device's Screen On
Eclipse Hangs at The Android Sdk Content Loader
Does 'Meta Name="Google-Play-App"' Work
Placeholder Text for an Input Type="Number" Does Not Show in Webkit Ics
Android Viewpager - Show Preview of Page on Left and Right
Android Device Configuration for Aosp
How to Set The Title Color for The New Toolbar
Android Httpclient File Upload Data Corruption and Timeout Issues
Scaling a Phonegap App for Different Android Screen Sizes/Densities
Is There a Workaround for The Android Browser Bug with CSS-Position and Clickable Areas
Android Imageview Change Tint to Simulate Button Click
How to Use Firebase Functions to Send Fcm to User
JSONarray Cannot Be Converted to JSONobject Error
Fragment View in Viewpager Is Not Restored When Resuming