Android show dialog causes illegal state exception if activity recreated - java

There are three activities: Login, Main and Profile
Obviously from Login you can go to Main. There is a Button that calls AppCompatDialogFragment in Main.
BalanceCalculationDialog dialog = BalanceCalculationDialog.newInstance(model);
dialog.setListener(this);
dialog.show(getSupportFragmentManager(), "TAG");
If I user enters Main for the first time dialog shows.
If user goes to Profile and then backs dialog shows.
If user goes out from Main (finish() with Intent#FLAG_ACTIVITY_NEW_TASK and Intent#FLAG_ACTIVITY_CLEAR_TASK flags) and tries to see dialog he sees an error.
java.lang.IllegalStateException: Can not perform this action after
onSaveInstanceState....CheckStateLoss()...
As far as I know it caused because "Transaction cannot be commited after onSaveInstanceState()".
I tried to override this method leaving empty body, not calling super and didn't work.
Then I found another way to solve problem, which worked:
#Override
public void show(FragmentManager manager, String tag) {
try {
FragmentTransaction ft = manager.beginTransaction();
ft.add(this, tag);
ft.commitAllowingStateLoss();
} catch (IllegalStateException e) {
Log.e("ILLEGAL", "Exception", e);
}
}
Nevertheless I don't understand why it works this way -> Number of catching IllegalStateException equals the number of recreating Main. For example I log out 20 times - then I will have 20 catching of mentioned exception. So I don't find this solution as the right one.
In addition, I have other activities that can call another dialogs. These activities also can be finished with finish() and when user opens one of them again he doesn't have the issue.
I would be very glad to have proper explanation of such behaviour and right way of solving it. Thank you very much.
EDIT
Reason of this issue was usage of RxJava EventBus. When I switched to Otto Eventbus problem disappeared. RxJava allows you to send and get messages without subscribe and unsubscribe that can cause unexpected behaviour.

The exception was thrown because you attempted to commit a FragmentTransaction after the activity’s state had been saved, resulting in a phenomenon known as Activity state loss. Before we get into the details of what this actually means, however, let’s first take a look at what happens under-the-hood when onSaveInstanceState() is called. Android applications have very little control over their destiny within the Android runtime environment. The Android system has the power to terminate processes at any time to free up memory, and background activities may be killed with little to no warning as a result. To ensure that this sometimes erratic behavior remains hidden from the user, the framework gives each Activity a chance to save its state by calling its onSaveInstanceState() method before making the Activity vulnerable to destruction. When the saved state is later restored, the user will be given the perception that they are seamlessly switching between foreground and background activities, regardless of whether or not the Activity had been killed by the system.
When the framework calls onSaveInstanceState(), it passes the method a Bundle object for the Activity to use to save its state, and the Activity records in it the state of its dialogs, fragments, and views. When the method returns, the system parcels the Bundle object across a Binder interface to the System Server process, where it is safely stored away. When the system later decides to recreate the Activity, it sends this same Bundle object back to the application, for it to use to restore the Activity’s old state.
So why then is the exception thrown? Well, the problem stems from the fact that these Bundle objects represent a snapshot of an Activity at the moment onSaveInstanceState() was called, and nothing more. That means when you call FragmentTransaction#commit() after onSaveInstanceState() is called, the transaction won’t be remembered because it was never recorded as part of the Activity’s state in the first place. From the user’s point of view, the transaction will appear to be lost, resulting in accidental UI state loss. In order to protect the user experience, Android avoids state loss at all costs, and simply throws an IllegalStateException whenever it occurs.
Solution for this exception is
Be careful when committing transactions inside Activity lifecycle methods. A large majority of applications will only ever commit transactions the very first time onCreate() is called and/or in response to user input, and will never face any problems as a result. However, as your transactions begin to venture out into the other Activity lifecycle methods, such as onActivityResult(), onStart(), and onResume(), things can get a little tricky. For example, you should not commit transactions inside the FragmentActivity#onResume() method, as there are some cases in which the method can be called before the activity’s state has been restored (see the documentation for more information). If your application requires committing a transaction in an Activity lifecycle method other than onCreate(), do it in either FragmentActivity#onResumeFragments() or Activity#onPostResume(). These two methods are guaranteed to be called after the Activity has been restored to its original state, and therefore avoid the possibility of state loss all together. (As an example of how this can be done, check out my answer to this StackOverflow question for some ideas on how to commit FragmentTransactions in response to calls made to the Activity#onActivityResult() method).
Avoid performing transactions inside asynchronous callback methods. This includes commonly used methods such as AsyncTask#onPostExecute() and LoaderManager.LoaderCallbacks#onLoadFinished(). The problem with performing transactions in these methods is that they have no knowledge of the current state of the Activity lifecycle when they are called. For example, consider the following sequence of events:
An activity executes an AsyncTask.
The user presses the “Home” key, causing the activity’s
onSaveInstanceState() and onStop() methods to be called.
The AsyncTask completes and onPostExecute() is called, unaware that the
Activity has since been stopped. A FragmentTransaction is committed
inside the onPostExecute() method, causing an exception to be thrown.
In general, the best way to avoid the exception in these cases is to
simply avoid committing transactions in asynchronous callback methods
all together.
Use commitAllowingStateLoss() only as a last resort. The only difference between calling commit() and commitAllowingStateLoss() is that the latter will not throw an exception if state loss occurs. Usually you don’t want to use this method because it implies that there is a possibility that state loss could happen. The better solution, of course, is to write your application so that commit() is guaranteed to be called before the activity’s state has been saved, as this will result in a better user experience. Unless the possibility of state loss can’t be avoided, commitAllowingStateLoss() should not be used.
But I have to agree that this is an Android framework design defect. Google devs try to fix it over many version. But seems like there is no good approach to work-around this. Just understand the life cycle and remember to check the state of activity carefully before committing transaction

Related

runOnUiThread and callbacks strange behavior

At runtime, I am using a callback pattern to have generic code run and use the callback when finished to execute code on the GUI. So in my handler I use the runOnUiThread function against the callback.
These are all async http calls. At runtime if I interrupt and use the back button and go to another fragment for example, the system will swap out the fragments and run both callbacks (the new fragment one on the correct callback, and the old callback that should have ran on the old fragment on the new current fragment). This is wrong, the new fragment gets both callbacks but when it was initialized it was assigned to the other fragment...
This makes no sense and you can only observe the behavior by switching fragments at runtime before an async call finishes. I don't see how it's possible, in the code I check if callback is null so it should have been garbage collected and how it runs on the new callback I don't know how it's possible....there's no additional assignment happening to change this. It's almost like it's just looking in memory for the function signature and running it even though when it does it's on the wrong object.
Does anyone have any clue what's going on? I've surpressed it with an ugly piece code, I just don't know how this is possible?
I understand the obvious candidates are if I assigned it again somewhere, but the answer is no. When the fragment is created it creates an object, assigns itself as the callback, and processing begins so when the fragment is destroyed it should be too. But the async task is still executing which is fine, why it does a callback on the new thread on the main gui I guess is because of runOnUiThread, so that function is somehow changing what the callback object points to in memory?
Its your code :-)
There is no magic happening 'behind the scenes' in Android and no references are changed.
Many have problems like yours, and its always because they assume that the Android system is somehow cleaning up when a fragment or activity instance is replaced or removed. It isn't, and therefore the callbacks are still executed, on a obsolete fragment or activity.
runOnUiThread takes a Runnable and runs it when the UI thread have spare time (after invoking lifecycle methods and finished rendering). Its not magic either :-)

WeakReference/AsyncTask pattern in android

I have a question regarding this simple frequently occurring situation in android .
We have a main activity , we invoke an AsyncTask alongwith the reference of the mainactivity , so that that the AsyncTask can update the views on the MainActivity.
I will break down the event into steps
MainActivity creates an AyncTask , passes its reference to it .
AysncTask , starts it's work , downloading ten files for example
The user changed the orientation of the device. This results in an orphan pointer in the AsyncTask
When the AsyncTask completes , and tries to access the activity to update the status , it crashes , because of the null pointer .
The solution for the above is to keep a WeakReference in the AsyncTask as recommended by the book "Pro Android 4"
WeakReference<Activity> weakActivity;
in method onPostExecute
Activity activity = weakActivity.get();
if (activity != null) {
// do your stuff with activity here
}
How does this resolve the situation ?
My question it , if my asynctask is downloading ten files , and upon completion of 5 the activity is restarted (because of an orientation change) then would my FileDownloadingTask be invoked once again ?.
What would happen to the previous AsyncTask that was initially invoked ?
Thank you , and I apologize for the length of the question .
How does this resolve the situation ?
The WeakReference allows the Activity to be garbage collected, so you don't have a memory leak.
A null reference means that the AsyncTask cannot blindly try to update a user-interface that is no longer attached, which would throw exceptions (e.g. view not attached to window manager). Of course you have to check for null to avoid NPE.
if my asynctask is downloading ten files , and upon completion of 5 the activity is restarted (because of an orientation change) then would my FileDownloadingTask be invoked once again ?.
Depends on your implementation, but probably yes - if you don't deliberately do something to make a repeat download unnecessary, such as caching the results somewhere.
What would happen to the previous AsyncTask that was initially invoked ?
In earlier versions of Android it would run to completion, downloading all of the files only to throw them away (or perhaps cache them, depending on your implementation).
In newer Android's I am suspicious that AsyncTask's are being killed along with the Activity that started them, but my basis for suspicion is only that the memory-leak demo's for RoboSpice (see below) do not actually leak on my JellyBean devices.
If I may offer some advice: AsyncTask is not suitable for performing potentially long running tasks such as networking.
IntentService is a better (and still relatively simple) approach, if a single worker thread is acceptable to you. Use a (local) Service if you want control over the thread-pool - and be careful not to do work on the main thread!
RoboSpice seems good if you are looking for a way to reliably perform networking in the background (disclaimer: I have not tried it; I am not affiliated). There is a RoboSpice Motivations demo app in the play store which explains why you should use it by demo-ing all the things that can go wrong with AsyncTask - including the WeakReference workaround.
See also this thread: Is AsyncTask really conceptually flawed or am I just missing something?
Update:
I created a github project with an example of downloading using IntentService for another SO question (How to fix android.os.NetworkOnMainThreadException?), but it is also relevant here, I think. It has the added advantage that, by returning the result via onActivityResult, a download that is in flight when you rotate the device will deliver to the restarted Activity.
The WeakReference class basically just prevents the JRE to increase the reference counter for the given instance.
I won't go into Java's memory management and answer your question directly: The WeakReference resolves the situation by providing the AsyncTask a way to learn if its parent activity is still valid.
The orientation change itself will not automatically restart the AsyncTask. You have to code the desired behavior with the known mechanisms (onCreate/onDestroy, onSave/RestoreInstanceState).
Concerning the original AsyncTask, I'm not 100 % sure which of these options will happen:
Either Java stops the thread and disposes the AsyncTask, because the only object holding a reference to it (the original Activity) is destroyed
Or some internal Java object maintains a reference to the AsyncTask object, blocking its garbage collection, effectively leaving the AsyncTask to finish in the background
Either way, it would be good practice to abort/pause and restart/resume the AsyncTask manually (or handing it over to the new Activity), or use a Service instead.
How does this resolve the situation ?
It doesn't.
The referent of a WeakReference is set to null when the garbage collector determines that the referent is weakly reachable. This does not happen when an activity is paused, and does not necessarily happen immediately when the activity is destroyed and the framework discards all references to it. If the GC has not run, it is entirely possible for the AsyncTask to complete while its WeakReference still contains a reference to a dead activity.
Not only that, but this approach does nothing to prevent the AsyncTask from uselessly consuming CPU.
A better approach is to have the Activity maintain a strong reference to the AsyncTask and cancel(...) it in the appropriate teardown lifecycle method. The AsyncTask should monitor isCancelled() and stop working if it is no longer needed.
If you want an AsyncTask to survive across configuration changes (but not other forms of activity destruction) you can host it in a retained fragment.

What is the best equivalent of a main() function for an Android app?

I've searched everywhere a manner to have the equivalent of a main() function (yes function not method) inside a Android application but failed...
Typically what I would want to do is:
void main()
{
// do some really nice initialisations stuff here
// ... let the app does his life, I really don't care
// do some final stuff here before leaving
}
The nearest approach I've seen so far is to use a SplashScreen and override the OnCreate() method. The problem is that is not acceptable from my point of view.
Why? Because a SplashScreen is nothing than an Activity tagged as a LAUNCHER.
That makes it to appear in the apps list, thing I don't want when I develop an app widget.
Furthermore, where to place my code just before the app destroy? In the onDestroy() method?
No, once again, this is not reliable. Android can decide to delete my instance whereas the application is still running.
Well, in fact, I take for principle that every components of my app are running in the same process since I don't mention explicitely in the Manifest that I wan't a component to run in its own process.
In the case of an app widget, I've placed my init code on the first call of onUpdate() method. I think it's a good bet. Then this app widget (AppWidgetProvider more precisely) is in charge to launch any activity as its will.
The "DataBase" for all the app is defined in a separate Singleton like this:
public class MyDataBase {
public static MyDataBase getInstance() {
if (instance_ == null)
instance_ = new DataBase();
return instance_;
}
public void load();
public void save();
static MyDataBase instance_ = null;
public int myInt;
public String myString;
public Object myObject;
etc..
}
With this Singleton I'm sure at least, its lifecycle is the same as the entire app itself.
To back with that AppWidgetProvider, I have to trick a little. Indeed, Android can decide to delete its instance whereas some other activities are still on place and the process is still running. So for example, systematically loading my DataBase in the first call of the OnUpdate() is unnecessary and overkill. What I do is having a static boolean value that indicates if the DataBase have been loaded for the lifecycle of this process or not.
Thus, the AppWidgetProvider can be instanciated tons of time, as long as the Singleton DataBase persists (so the process), it will not reload the DataBase each time, got it?
(yes difficult to be clear...)
About the cleanup code of the app, I thought to override the finalize() method of my DataBase Singleton, but well, I'm really not sure it's a good idea since the moment of the call of this method is totally unpredictable. I suppose it would be called if you suddently power off your Android, but well I'm not sure of anything here, thus so far, I didn't found a solution for that part.
Any comment or something less tricky that what I currently do is welcome.
Thanks.
onResume() is the function that will be invariably reached before starting you app, so you could either put the 'main' code in the onCreate() method or the onResume().
onPause() is ALWAYS called before destroying the app, either by the user or the OS.
There is great explanation regarding the lifecycle in the Android documentation:
http://developer.android.com/training/basics/activity-lifecycle/starting.html
For the initialisation you can over-ride the onCreate method of the Application class:
Called when the application is starting, before any activity, service, or receiver objects (excluding content providers) have been created. Implementations should be as quick as possible (for example using lazy initialization of state) since the time spent in this function directly impacts the performance of starting the first activity, service, or receiver in a process. If you override this method, be sure to call super.onCreate()
Termination is harder to deal with. You'll probably have to monitor each component of your application separately. If you are targeting API level 14 or later you can use Application.registerActivityLifecycleCallbacks to help with this.
The nearest approach I've seen so far is to use a SplashScreen and
override the OnCreate() method. The problem is that is not acceptable
from my point of view. Why? Because a SplashScreen is nothing than an
Activity tagged as a LAUNCHER.
It's because Android is formed with several activities and those activities have life cycles. So every activities start from onCreate() then finishes at onDestroy().
http://developer.android.com/training/basics/activity-lifecycle/starting.html
That makes it to appear in the apps list, thing I don't want when I
develop an app widget. Furthermore, where to place my code just before
the app destroy? In the onDestroy() method? No, once again, this is
not reliable. Android can decide to delete my instance whereas the
application is still running.
In a scenario that user presses home button to exit from your app, your current activity is more likely to invoke onPause() method (only when the activity has no other processes to finish). However, when user force closes (terminates) your whole application by ending process. Then you don't have to worry about invoking any methods or what so ever because Android itself will close anything related to your application automatically.
To back with that AppWidgetProvider, I have to trick a little. Indeed,
Android can decide to delete its instance whereas some other
activities are still on place and the process is still running. So for
example, systematically loading my DataBase in the first call of the
OnUpdate() is unnecessary and overkill. What I do is having a static
boolean value that indicates if the DataBase have been loaded for the
lifecycle of this process or not. Thus, the AppWidgetProvider can be
instanciated tons of time, as long as the Singleton DataBase persists
(so the process), it will not reload the DataBase each time, got it?
(yes difficult to be clear...)
The example you have posted for singleton database connection is not bad I think, but there are better ways to get the job done cleanly and more effectively. For example hibernate framework connection pooling

Android: overriding onPause and onResume - proper way

When overriding the onPause() and the onResume() methods of the activity, where is the proper location to call the super.onPause() and super.onResume()? At the beginning of the method or at the end?
From http://developer.android.com/guide/components/activities.html#ImplementingLifecycleCallbacks:
Your implementation of these lifecycle methods must always call the superclass implementation before doing any work, as shown in the examples above.
So, for lifecycle callbacks, like onPause() and onResume(), we should do super.onPause() or super.onResume() at the very beginning. For other methods, it all depends on the semantics of the super class.
Update: This is the accepted answer and it contains a nice amount of good information, including a useful diagram, pulled together into one place. However, it appears to be incorrect, at least according to the current Android documentation which, as the poster points out, is the ultimate source for information on the SDK. Possibly the documentation was clarified after this answer was posted. But, in any case, don't stop reading with this answer, check out espinchi's answer below. It has the documentation on its side.
Placement of the super methods depends only on your preference. It would only matter if those methods were taking parameters OR if you were doing some concurrent work. For example if you do this:
#Override
protected void onPause() {
try {
someOtherThread.join();
} catch (InterruptedException e) {
LOG.e(e);
}
super.onPause();
}
it might block the thread and prevent super from being called.
I suggest that you should read all documentation available because they will help you much. For example this is what you can find in the onPause javadoc. I bolded out the important parts:
Called as part of the activity lifecycle when an activity is going
into the background, but has not (yet) been killed. The counterpart to
onResume().
When activity B is launched in front of activity A, this callback will
be invoked on A. B will not be created until A's onPause() returns, so
be sure to not do anything lengthy here.
This callback is mostly used for saving any persistent state the
activity is editing, to present a "edit in place" model to the user
and making sure nothing is lost if there are not enough resources to
start the new activity without first killing this one. This is also a
good place to do things like stop animations and other things that
consume a noticeable amount of CPU in order to make the switch to the
next activity as fast as possible, or to close resources that are
exclusive access such as the camera.
In situations where the system needs more memory it may kill paused
processes to reclaim resources. Because of this, you should be sure
that all of your state is saved by the time you return from this
function. In general onSaveInstanceState(Bundle) is used to save
per-instance state in the activity and this method is used to store
global persistent data (in content providers, files, etc.)
After receiving this call you will usually receive a following call to
onStop() (after the next activity has been resumed and displayed),
however in some cases there will be a direct call back to onResume()
without going through the stopped state.
Derived classes must call through to the super class's implementation
of this method. If they do not, an exception will be thrown.
I do recommend this flowchart for you it will help your development tremendously:
It probably doesn't matter, but to know for sure, you need to know what the super methods are doing, and usually that information is not available to you.
My style is to call e.g. super.onCreate(), super.onResume(), etc. before the body of my own method, and e.g. super.onPause() and super.onDestroy() after the body of my own method.
The theory behind this is that I like to let the super methods run first while building something up, just in case what I'm doing depends on what the superclass sets up first, and when tearing something down, I like to tear down my own stuff before the superclass tears down its stuff.
There's no right or wrong.
That depends on what you do on your implementation of these methods.
Sometimes you'll want the super to be before your code, and sometime after.
You can put it anywhere. First you have to understand the Activity life cycle. Check the following link Here
Download the demo and run it you will be clear
Delving into the android code, you can find that the framework sets a flag called mcalled when you call super.onPause(). This flag is later checked on resume by the framework.
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onResume()");
}
All you need to do is make sure the call is made to super and you are good. No other precaution is necessary.

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

There are numerous posts about how to handle a configuration change during an AsyncTask, but none I have found give a clear solution regarding apps that are in background (onPause()) when an AsyncTask finishes and tries to dismiss a DialogFragment (compatibility library).
Here is the problem, if I have an AsyncTask running that should dismiss a DialogFragment in onPostExecute(), I get an IllegalStateException if the app is in the background when it tries to dismiss the DialogFragment.
private static class SomeTask extends AsyncTask<Void, Void, Boolean> {
public SomeTask(SomeActivity tActivity)
{
mActivity = tActivity;
}
private SomeActivity mActivity;
/** Set the view during/after config change */
public void setView(Activity tActivity) {
mActivity tActivity;
}
#Override
protected Boolean doInBackground(Void... tParams) {
try {
//simulate some time consuming process
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException ignore) {}
return true;
}
#Override
protected void onPostExecute(Boolean tRouteFound) {
mActivity.dismissSomeDialog();
}
}
The Activity looks like this:
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
public class SomeActivity extends FragmentActivity {
public void someMethod() {
...
displaySomeDialog();
new SomeTask(this).execute();
...
}
public void displaySomeDialog() {
DialogFragment someDialog = new SomeDialogFragment();
someDialog.show(getFragmentManager(), "dialog");
}
public void dismissSomeDialog() {
SomeDialogFragment someDialog = (SomeDialogFragment) getFragmentManager().findFragmentByTag("dialog");
someDialog.dismiss();
}
....
}
Works fine UNLESS the app switches to background while SomeTask is still running. In that case, when SomeTask tries to dismissSomeDialog(), I get an IllegalStateException.
05-25 16:36:02.237: E/AndroidRuntime(965): java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
All of the posts I've seen seem to point in some kludgy direction with elaborate workarounds. Isn't there some android way of handling this? If it were a Dialog instead of a DialogFragment, then the Activity's dismissDialog() would handle it correctly. If it were a real DialogFragment instead of one from the ACP, then dismissAllowingStateLoss() would handle it. Isn't there something like this for the ACP version of DialogFragment?
Fragments are saved as part of each Activity's state, so performing transactions after onSaveInstanceState() has been called technically doesn't make sense.
You definitely don't want to use commitAllowingStateLoss() to avoid the exception in this case. Consider this scenario as an example:
The Activity executes an AsyncTask. The AsyncTask shows a DialogFragment in onPreExecute() and starts executing its task on a background thread.
The user clicks "Home" and the Activity is stopped and forced into the background. The system decides that the device is pretty low on memory so it decides that it should also destroy the Activity too.
The AsyncTask completes and onPostExecute() is called. Inside onPostExecute() you dismiss the DialogFragment using commitAllowingStateLoss() to avoid the exception.
The user navigates back to the Activity. The FragmentManager will restore the state of its fragments based on the Activity's saved state. The saved state doesn't know about anything after onSaveInstanceState() has been called, so the request to dismiss the DialogFragment will not be remembered and the DialogFragment will be restored even though the AsyncTask has already completed.
Because of weird bugs like these that can occasionally happen, it's usually not a good idea to use commitAllowingStateLoss() to avoid this exception. Because the AsyncTask callback methods (which are called in response to a background thread finishing its work) have absolutely nothing to do with the Activity lifecycle methods (which are invoked by the system server process in response to system-wide external events, such as the device falling asleep, or memory running low), handling these situations require you to do a little extra work. Of course, these bugs are extremely rare, and protecting your app against them will often not be the difference between a 1 star rating and a 5 star rating on the play store... but it is still something to be aware of.
Hopefully that made at least some sense. Also, note that Dialogs also exist as part of the Activitys state, so although using a plain old Dialog might avoid the exception, you would essentially have the same problem (i.e. dismissing the Dialog wouldn't be remembered when the Activity's state is later restored).
To be frank, the best solution would be to avoid showing a dialog throughout the duration of the AsyncTask. A much more user-friendly solution would be to show a indeterminate progress spinner in the ActionBar (like the G+ and Gmail apps, for example). Causing major shifts in the user interface in response to asynchronous callbacks is bad for the user experience because it is unexpected and abruptly yanks the user out of what they are doing.
See this blog post on the subject for more information.
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.
You should cancel your AsyncTask in onPause() if the onPostExecute() is going to update the UI. You shouldn't try to update the UI while your activity has been paused.
Eg. in your onPause():
if (task != null) task.cancel(true);
If you want the changes from the task to persist to the next time, then store the data/changes in doInBackground() and then update the UI when your activity/fragment/dialog gets resumed.
If you don't want the changes from the task to persist, then don't store the changes until onPostExecute()
When Android stops your app because the user hit the back or home button, your dialogs are closed for you. Usually the trick is to preserve the dialogs between onStop()/onStart(). So unless you need to do more than just close the dialog, I'd say don't worry about it.
EDIT: On your activity that hosts the dialog, you may still want to close the dialog if it's still open inside onStop(). This helps prevent memory leaks. But this doesn't need to be triggered from AsyncTask.
Like i said above, the problem is what happens when you hit onStart() again and your AsyncTask is NOT finished yet. You'll need to figure out a way to determine that and re-open that dialog if needed.
After numerous redesigns I finally settled on an approach that seems to work. However, I haven't been able to find certain aspects of my design documented elsewhere in discussions of Android development. So I'd be interested in any feedback.
In summary, what I had to do is this:
onSaveInstanceState() - if SomeTask is running, I use a simple lock to block SomeTask from exiting doInBackground() during pause.
onResume() - I had to put in a switch statement to handle different resume situations. If launching app I do nothing as nothing is paused, if restarting after being hidden or after config change I release the lock so that the preserved SomeTask instance can resume where it left off, etc.
onDestroy() - I cancel SomeTask if it is running.
I'll put the code fragments for this solution in my original post.
getActivity().finish(); in the DialogFragment worked for me.

Categories

Resources