Why is Fragment null inside onActivityResult - android

Here is my setup:
Activity -> FragmentWithPages -> ViewPager{Fragment1, Fragment2}
From Fragment1 I launch a DialogFragment and then from the DialogFragment I launch activityForResult for an implicit camera intent to take picture.
Problem:
Sometimes, when returning from the camera my app crashes inside the onActivityResult of Fragment1. Why does this happen? Now understand the chain of callback of onActivityResult. It would be coming back in the order of Activity.onActivityResult -> FragmentWithPages.onActivityResult -> Fragment1.onActivityResult -> DialogFragment.onActivityResult. So my question is, why is DialogFragment null when I do mDialogFragment.onActivityResult(…)?
I imagine it might have to do with memory: the system kills my app and then restarts it after the Camera app returns. But if that were the case, why is the DialogFragment the broken link in the chain? What can I do to prevent this problem or handle it as if nothing went wrong?
So no I do not mean to simply catch the NPE, that does not really do much as it would void the whole purpose for which I took the picture.

When you return from a startActivityForResult(), there's no guarantee that the activity instance will be the same. The system is free to dispose of the activity that started for result, and reconstruct it to handle the onActivityResult() call.
If this happens, it means of course that any instance fields you had previously assigned will no longer be so. It's up to you to use onSaveInstanceState() and reconstruct your activity instance using the saved instance state Bundle passed in to your onCreate().
You can test this scenario by setting "don't keep activities" in developer settings on your device.

This is a common situation where the system destroys your activity because it needs more resources. You need to implement the onSaveInstanceState method and then restore the instance state in the onCreate or onRestoreInstanceState method.
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putExtra("yourData", yourParcelableData);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// keep the fragment and all its data across screen rotation
setRetainInstance(true);
if(savedInstanceState != null){
yourParcelableData = (yourParcelableData)savedInstanceState.getExtra("yourData");
}
}
There is a library that allows you to do it easier.
https://github.com/frankiesardo/icepick

Related

SplashScreen crashes when device screen is turned off [duplicate]

I have a Live Android application, and from market i have received following stack trace and i have no idea why its happening as its not happening in application code but its getting caused by some or the other event from the application (assumption)
I am not using Fragments, still there is a reference of FragmentManager.
If any body can throw some light on some hidden facts to avoid this type of issue:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyDown(Activity.java:1962)
at android.view.KeyEvent.dispatch(KeyEvent.java:2482)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1720)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1258)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2851)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2824)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2011)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4025)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
at dalvik.system.NativeStart.main(Native Method)
This is the most stupid bug I have encountered so far. I had a Fragment application working perfectly for API < 11, and Force Closing on API > 11.
I really couldn't figure out what they changed inside the Activity lifecycle in the call to saveInstance, but I here is how I solved this :
#Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
I just do not make the call to .super() and everything works great. I hope this will save you some time.
EDIT: after some more research, this is a known bug in the support package.
If you need to save the instance, and add something to your outState Bundle you can use the following :
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
super.onSaveInstanceState(outState);
}
EDIT2: this may also occur if you are trying to perform a transaction after your Activity is gone in background. To avoid this you should use commitAllowingStateLoss()
EDIT3: The above solutions were fixing issues in the early support.v4 libraries from what I can remember. But if you still have issues with this you MUST also read #AlexLockwood 's blog : Fragment Transactions & Activity State Loss
Summary from the blog post (but I strongly recommend you to read it) :
NEVER commit() transactions after onPause() on pre-Honeycomb, and onStop() on post-Honeycomb
Be careful when committing transactions inside Activity lifecycle methods. Use onCreate(), onResumeFragments() and onPostResume()
Avoid performing transactions inside asynchronous callback methods
Use commitAllowingStateLoss() only as a last resort
Looking in Android source code on what causes this issue gives that flag mStateSaved in FragmentManagerImpl class (instance available in Activity) has value true. It is set to true when the back stack is saved (saveAllState) on call from Activity#onSaveInstanceState.
Afterwards the calls from ActivityThread don't reset this flag using available reset methods from FragmentManagerImpl#noteStateNotSaved() and dispatch().
The way I see it there are some available fixes, depending on what your app is doing and using:
Good ways
Before anything else: I would advertise Alex Lockwood article. Then, from what I've done so far:
For fragments and activities that don't need to keep any state information, call commitAllowStateLoss. Taken from documentation:
Allows the commit to be executed after an activity's state is saved. This is dangerous because the commit can be lost if the activity needs to later be restored from its state, so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user`. I guess this is alright to use if the fragment is showing read-only information. Or even if they do show editable info, use the callbacks methods to retain the edited info.
Just after the transaction is commit (you just called commit()), make a call to FragmentManager.executePendingTransactions().
Not recommended ways:
As Ovidiu Latcu mentioned above, don't call super.onSaveInstanceState(). But this means you will lose the whole state of your activity along with fragments state.
Override onBackPressed and in there call only finish(). This should be OK if you application doesn't use Fragments API; as in super.onBackPressed there is a call to FragmentManager#popBackStackImmediate().
If you are using both Fragments API and the state of your activity is important/vital, then you could try to call using reflection API FragmentManagerImpl#noteStateNotSaved(). But this is a hack, or one could say it's a workaround. I don't like it, but in my case it's quite acceptable since I have a code from a legacy app that uses deprecated code (TabActivity and implicitly LocalActivityManager).
Below is the code that uses reflection:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
invokeFragmentManagerNoteStateNotSaved();
}
#SuppressWarnings({ "rawtypes", "unchecked" })
private void invokeFragmentManagerNoteStateNotSaved() {
/**
* For post-Honeycomb devices
*/
if (Build.VERSION.SDK_INT < 11) {
return;
}
try {
Class cls = getClass();
do {
cls = cls.getSuperclass();
} while (!"Activity".equals(cls.getSimpleName()));
Field fragmentMgrField = cls.getDeclaredField("mFragments");
fragmentMgrField.setAccessible(true);
Object fragmentMgr = fragmentMgrField.get(this);
cls = fragmentMgr.getClass();
Method noteStateNotSavedMethod = cls.getDeclaredMethod("noteStateNotSaved", new Class[] {});
noteStateNotSavedMethod.invoke(fragmentMgr, new Object[] {});
Log.d("DLOutState", "Successful call for noteStateNotSaved!!!");
} catch (Exception ex) {
Log.e("DLOutState", "Exception on worka FM.noteStateNotSaved", ex);
}
}
Cheers!
Such an exception will occur if you try to perform a fragment transition after your fragment activity's onSaveInstanceState() gets called.
One reason this can happen, is if you leave an AsyncTask (or Thread) running when an activity gets stopped.
Any transitions after onSaveInstanceState() is called could potentially get lost if the system reclaims the activity for resources and recreates it later.
Simply call super.onPostResume() before showing your fragment or move your code in onPostResume() method after calling super.onPostResume(). This solve the problem!
This can also happen when calling dismiss() on a dialog fragment after the screen has been locked\blanked and the Activity + dialog's instance state has been saved. To get around this call:
dismissAllowingStateLoss()
Literally every single time I'm dismissing a dialog i don't care about it's state anymore anyway, so this is ok to do - you're not actually losing any state.
Short And working Solution :
Follow Simple Steps :
Step 1 : Override onSaveInstanceState state in respective fragment. And remove super method from it.
#Override
public void onSaveInstanceState(Bundle outState) {
};
Step 2 : Use CommitAllowingStateLoss(); instead of commit(); while fragment operations.
fragmentTransaction.commitAllowingStateLoss();
I think Lifecycle state can help to prevent such crash starting from Android support lib v26.1.0 you can have the following check:
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)){
// Do fragment's transaction commit
}
or you can try:
Fragment.isStateSaved()
more information here
https://developer.android.com/reference/android/support/v4/app/Fragment.html#isStateSaved()
this worked for me... found this out on my own... hope it helps you!
1) do NOT have a global "static" FragmentManager / FragmentTransaction.
2) onCreate, ALWAYS initialize the FragmentManager again!
sample below :-
public abstract class FragmentController extends AnotherActivity{
protected FragmentManager fragmentManager;
protected FragmentTransaction fragmentTransaction;
protected Bundle mSavedInstanceState;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSavedInstanceState = savedInstanceState;
setDefaultFragments();
}
protected void setDefaultFragments() {
fragmentManager = getSupportFragmentManager();
//check if on orientation change.. do not re-add fragments!
if(mSavedInstanceState == null) {
//instantiate the fragment manager
fragmentTransaction = fragmentManager.beginTransaction();
//the navigation fragments
NavigationFragment navFrag = new NavigationFragment();
ToolbarFragment toolFrag = new ToolbarFragment();
fragmentTransaction.add(R.id.NavLayout, navFrag, "NavFrag");
fragmentTransaction.add(R.id.ToolbarLayout, toolFrag, "ToolFrag");
fragmentTransaction.commitAllowingStateLoss();
//add own fragment to the nav (abstract method)
setOwnFragment();
}
}
I was always getting this when I tried to show fragment in onActivityForResult() method, so the problem was next:
My Activity is paused and stopped, which means, that onSaveInstanceState() was called already (for both pre-Honeycomb and post-Honeycomb devices).
In case of any result I made transaction to show/hide fragment, which causes this IllegalStateException.
What I made is next:
Added value for determining if action I want was done (e.g. taking photo from camere - isPhotoTaken) - it can be boolean or integer value depending how much different transactions you need.
In overriden onResumeFragments() method I checked for my value and after made fragment transactions I needed. In this case commit() was not done after onSaveInstanceState, as state was returned in onResumeFragments() method.
I solved the issue with onconfigurationchanged. The trick is that according to android activity life cycle, when you explicitly called an intent(camera intent, or any other one); the activity is paused and onsavedInstance is called in that case. When rotating the device to a different position other than the one during which the activity was active; doing fragment operations such as fragment commit causes Illegal state exception. There are lots of complains about it. It's something about android activity lifecycle management and proper method calls.
To solve it I did this:
1-Override the onsavedInstance method of your activity, and determine the current screen orientation(portrait or landscape) then set your screen orientation to it before your activity is paused. that way the activity you lock the screen rotation for your activity in case it has been rotated by another one.
2-then , override onresume method of activity, and set your orientation mode now to sensor so that after onsaved method is called it will call one more time onconfiguration to deal with the rotation properly.
You can copy/paste this code into your activity to deal with it:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Toast.makeText(this, "Activity OnResume(): Lock Screen Orientation ", Toast.LENGTH_LONG).show();
int orientation =this.getDisplayOrientation();
//Lock the screen orientation to the current display orientation : Landscape or Potrait
this.setRequestedOrientation(orientation);
}
//A method found in stackOverflow, don't remember the author, to determine the right screen orientation independently of the phone or tablet device
public int getDisplayOrientation() {
Display getOrient = getWindowManager().getDefaultDisplay();
int orientation = getOrient.getOrientation();
// Sometimes you may get undefined orientation Value is 0
// simple logic solves the problem compare the screen
// X,Y Co-ordinates and determine the Orientation in such cases
if (orientation == Configuration.ORIENTATION_UNDEFINED) {
Configuration config = getResources().getConfiguration();
orientation = config.orientation;
if (orientation == Configuration.ORIENTATION_UNDEFINED) {
// if height and widht of screen are equal then
// it is square orientation
if (getOrient.getWidth() == getOrient.getHeight()) {
orientation = Configuration.ORIENTATION_SQUARE;
} else { //if widht is less than height than it is portrait
if (getOrient.getWidth() < getOrient.getHeight()) {
orientation = Configuration.ORIENTATION_PORTRAIT;
} else { // if it is not any of the above it will defineitly be landscape
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
}
}
}
return orientation; // return value 1 is portrait and 2 is Landscape Mode
}
#Override
public void onResume() {
super.onResume();
Toast.makeText(this, "Activity OnResume(): Unlock Screen Orientation ", Toast.LENGTH_LONG).show();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
I had the same problem, getting IllegalStateException, but replacing all my calls to commit() with commitAllowingStateLoss() did not help.
The culprit was a call to DialogFragment.show().
I surround it with
try {
dialog.show(transaction, "blah blah");
}
catch(IllegalStateException e) {
return;
}
and that did it. OK, I don't get to show the dialog, but in this case that was fine.
It was the only place in my app where I first called FragmentManager.beginTransaction() but never called commit() so I did not find it when I looked for "commit()".
The funny thing is, the user never leaves the app. Instead the killer was an AdMob interstitial ad showing up.
My solution for that problem was
In fragment add methods:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
guideMapFragment = (SupportMapFragment)a.getSupportFragmentManager().findFragmentById(R.id.guideMap);
guideMap = guideMapFragment.getMap();
...
}
#Override
public void onDestroyView() {
SherlockFragmentActivity a = getSherlockActivity();
if (a != null && guideMapFragment != null) {
try {
Log.i(LOGTAG, "Removing map fragment");
a.getSupportFragmentManager().beginTransaction().remove(guideMapFragment).commit();
guideMapFragment = null;
} catch(IllegalStateException e) {
Log.i(LOGTAG, "IllegalStateException on exit");
}
}
super.onDestroyView();
}
May be bad, but couldn't find anything better.
I got this issue.But I think this problem is not related to commit and commitAllowStateLoss.
The following stack trace and exception message is about commit().
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
But this exception was caused by onBackPressed()
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(Unknown Source)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(Unknown Source)
at android.support.v4.app.FragmentActivity.onBackPressed(Unknown Source)
They were all caused by checkStateLoss()
private void checkStateLoss() {
if (mStateSaved) {
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
if (mNoTransactionsBecause != null) {
throw new IllegalStateException(
"Can not perform this action inside of " + mNoTransactionsBecause);
}
mStateSaved will be true after onSaveInstanceState.
This problem rarely happens.I have never encountered this problem.I can not reoccurrence the problem.
I found issue 25517
It might have occurred in the following circumstances
Back key is called after onSaveInstanceState, but before the new activity is started.
use onStop() in code
I'm not sure what the root of the problem is.
So I used an ugly way.
#Override
public void onBackPressed() {
try{
super.onBackPressed();
}catch (IllegalStateException e){
// can output some information here
finish();
}
}
I have got the same issue in my App. I have been solved this issue just calling the super.onBackPressed(); on previous class and calling the commitAllowingStateLoss() on the current class with that fragment.
onSaveInstance will be called if a user rotates the screen so that it can load resources associated with the new orientation.
It's possible that this user rotated the screen followed by pressing the back button (because it's also possible that this user fumbled their phone while using your app)
Another lifecycle way to solve the issue is to use the latest released lifecycle-ktx with kotlin.
lifecycleScope.launchWhenResumed {
// your code with fragment or dialogfragment
}
The closure will be run after resume state, so even this method is called after
stop, it'll be safly excuted when the next resume come.
You can also choose like
lifecycleScope.launchWhenCreated
// or
lifecycleScope.launchWhenStarted
to fit your situation.
The code will be cancelled when destroy is come.
The Google document link:
https://developer.android.com/kotlin/ktx#lifecycle
Read
http://chris-alexander.co.uk/on-engineering/dev/android-fragments-within-fragments/
article.
fragment.isResumed() checking helps me in onDestroyView w/o using onSaveInstanceState method.
Same issue from me and after a day long analysis of all articles, blog and stackoverflow i've found a simple solution. Don't use savedInstanceState at all, this is the condition with one line of code. On the fragment code:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
.....
This happens whenever you are trying to load a fragment but the activity has changed its state to onPause().This happens for example when you try to fetch data and load it to the activity but by the time the user has clicked some button and has moved to next activity.
You can solve this in two ways
You can use transaction.commitAllowingStateLoss() instead of transaction.commit() to load fragment but you may end up losing commit operation that is done.
or
Make sure that activity is in resume and not going to pause state when loading a fragment.
Create a boolean and check if activity is not going to onPause() state.
#Override
public void onResume() {
super.onResume();
mIsResumed = true;
}
#Override
public void onPause() {
mIsResumed = false;
super.onPause();
}
then while loading fragment check if activity is present and load only when activity is foreground.
if(mIsResumed){
//load the fragment
}
Thanks #gunar, but I think there is a better way.
According to doc :
* If you are committing a single transaction that does not modify the
* fragment back stack, strongly consider using
* {#link FragmentTransaction#commitNow()} instead. This can help avoid
* unwanted side effects when other code in your app has pending committed
* transactions that expect different timing.
*
* #return Returns true if there were any pending transactions to be
* executed.
*/
public abstract boolean executePendingTransactions();
So use commitNow to replace:
fragmentTransaction.commit();
FragmentManager.executePendingTransactions()
Well, after trying all the above solutions without success (because basically i dont have transactions).
On my case i was using AlertDialogs and ProgressDialog as fragments that, sometimes, on rotation, when asking for the FragmentManager, the error rises.
I found a workaround mixing some many similar posts:
Its a 3 step solution, all done on your FragmentActivity (in this case, its called GenericActivity):
private static WeakReference<GenericActivity> activity = null; //To avoid bug for fragments: Step 1 of 3
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
//To avoid bug for fragments: Step 2 of 3
activity = new WeakReference<GenericActivity>(this);
}
#Override
public FragmentManager getSupportFragmentManager(){
//To avoid bug for fragments: Step 3 of 3
if (this == activity.get()) {
return super.getSupportFragmentManager();
}
return activity.get().getSupportFragmentManager();
}
When i use startactivity in one fragment, i will get this exception;
When i change to use startactivityforresult, the exception is gone :)
So the easy way to fix it is use the startActivityForResult api :)
I was getting this exception when I was pressing back button to cancel intent chooser on my map fragment activity.
I resolved this by replacing the code of onResume()(where I was initializing the fragment and committing transaction) to onStart() and the app is working fine now.
Hope it helps.
This is fixed in Android 4.2 and also in the support library's source.[*]
For details of the cause (and work-arounds) refer to the the Google bug report:
http://code.google.com/p/android/issues/detail?id=19917
If you're using the support library then you shouldn't have to worry about this bug (for long)[*]. However, if you're using the API directly (i.e. Not using the support library's FragmentManager) and targeting an API below Android 4.2 then you will need to try one of the work-arounds.
[*] At the time of writing the Android SDK Manager is still distributing an old version that exhibits this bug.
Edit I'm going to add some clarification here because I've obviously somehow confused whoever down-voted this answer.
There are several different (but related) circumstances that can cause this exception to be thrown. My answer above is referring to the specific instance discussed in the question i.e. a bug in Android which has subsequently been fixed. If you're getting this exception for another reason it's because you're adding/removing fragments when you shouldn't be (after fragment states have been saved). If you're in such a situation then perhaps "Nested Fragments - IllegalStateException “Can not perform this action after onSaveInstanceState”" can be of use to you.
After researching a bit the solution to this problem is to do your fragment commits in the onresume.
Source: https://wenchaojames.wordpress.com/2013/01/12/illegalstateexception-from-onactivityresult/
My use case: I have used listener in fragment to notify activity that some thing happened. I did new fragment commit on callback method. This works perfectly fine on first time. But on orientation change the activity is recreated with saved instance state. In that case fragment is not created again implies that the fragment have the listener which is old destroyed activity. Any way the call back method will get triggered on action. It goes to destroyed activity which cause the issue. The solution is to reset the listener in fragment with current live activity. This solve the problem.
What I found is that if another app is dialog type and allows touches to be sent to background app then almost any background app will crash with this error.
I think we need to check every time a transaction is performed if the instance was saved or restored.
In my case, with the same error exception, i put the "onBackPressed()" in a runnable (you can use any of your view):
myView.post(new Runnable() {
#Override
public void run() {
onBackPressed()
}
});
I do not understand why, but it works!
You may be calling fragmentManager.popBackStackImmediate(); when activity is paused. Activity is not finished but is paused and not on foreground. You need to check whether activity is paused or not before popBackStackImmediate().
I noticed something very interesting. I have in my app the option to open the phone's gallery and the device asks what app to use, there I click on the gray area away from the dialog and saw this issue. I noticed how my activity goes from onPause, onSaveInstanceState back to onResume, it doesn't happen to visit onCreateView. I am doing transactions at onResume. So what I ended up doing is setting a flag being negated onPause, but being true onCreateView. if the flag is true onResume then do onCommit, otherwise commitAllowingStateLoss. I could go on and waste so much time but I wanted to check the lifecycle. I have a device which is sdkversion 23, and I don't get this issue, but I have another one which is 21, and there I see it.

confused about android example code

I'm looking over some code on the android developer's site and have a quick question about the example show here - http://developer.android.com/guide/components/fragments.html
In particular, I'm looking at this piece of code -
public static class DetailsActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE) {
// If the screen is now in landscape mode, we can show the
// dialog in-line with the list so we don't need this activity.
finish();
return;
}
if (savedInstanceState == null) {
// During initial setup, plug in the details fragment.
DetailsFragment details = new DetailsFragment();
details.setArguments(getIntent().getExtras());
getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
}
}
What is the point of the second if statement -
if (savedInstanceState == null) {
I can't find any situation where this if statement wouldn't be true. I've tested this code by adding an else statement and setting a breakpoint in it. I could not get to that breakpoint no matter I tried. So why even bother with an if statement? Why not leave it out all together?
There are situations in which your Activity is stopped by the Android operating system. In those cases, you get a chance to save the state of your Activity by a call to [onSaveInstanceState](http://developer.android.com/reference/android/app/Activity.html#onSaveInstanceState(android.os.Bundle). If after this, your Activity is started again, it'll be passed the Bundle you created so that you can restore the state properly.
You have to look at the complete example code. With this part it makes sense.
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("curChoice", mCurCheckPosition);
}
If you start your Activity the first time the Bundle savedInstanceState will be null and the body of the if statement will be executed. If onSaveInstanceState is called, because you navigated away from the Activity, the Bundle isn't null anymore and the if body will be not executed.
If your app was paused/killed, etc and you saved state by onSaveInstanceState then
savedInstanceState will contain the state of your app that you saved. Otherwise it will be null.
Apparently this was added to the example for future expansion on this code. While it has absolutely no functionality as it stands right now, if this activity were to launch another activity and get killed while the new activity had focus, this code would rebuild the activity when the user hits the back button, rather than rebuilding from scratch.

FragmentActivity onSaveInstanceState not getting called

I have seen a few similar questions about onSaveInstanceState not getting called for Fragments, but in my case Fragments work fine, it's the main FragmentActivity that's having trouble.
The relevant code looks fairly simple:
public class MyFActivity extends FragmentActivity implements ActionBar.TabListener {
String[] allValues; // data to save
#Override
protected void onSaveInstanceState (Bundle outState) {
Log.d("putting it!", allValues.toString());
outState.putStringArray("allValues", allValues);
super.onSaveInstanceState(outState);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
allValues = savedInstanceState.getStringArray("allValues");
Log.d("getting it!", allValues.toString());
}
}
}
When pausing the activity (using the back button), the onSaveInstanceState is never called, and consequently, savedInstanceState is always null within the onCreate method upon resuming the app. I tried adding a block like this:
#Override
public void onPause() {
super.onPause();
onSaveInstanceState(new Bundle());
}
which was suggested in https://stackoverflow.com/a/14195202/362657 but while onSaveInstanceState then gets called, savedInstanceState remains null within onCreate method. What am I missing?
The issue here is that you are misunderstanding how onSaveInstanceState works. It is designed to save the state of the Activity/Fragment in the case that the OS needs to destroy it for memory reasons or configuration changes. This state is then passed back in onCreate when the Activity/Fragment is returned to / restarted.
In a Fragment, all of their lifecycle callbacks are directly tied to their parent Activity. So onSaveInstanceState gets called on the Fragment when its parent Activity has onSaveInstanceState called.
When pausing the activity (using the back button), the onSaveInstanceState is never called, and consequently, savedInstanceState is always null within the onCreate method upon resuming the app.
When pressing back, the user is destroying the Activity, and therefore its children Fragments, so there is no reason to call onSaveInstanceState, since the instance is being destroyed. When you reopen the Activity, it's a brand new instance, with no saved state, so the Bundle passed in onCreate is null. This is behaving exactly as designed. However, try rotating the device or hitting the home button, then you will see the Activity and its children Fragments have onSaveInstanceState called, and passed back in onCreate when returned to.
The hack you added, directly calling onSaveInstanceState(new Bundle()); inside of onPause, is a very bad practice, as you should never call the lifecycle callbacks directly. Doing so can put your app into illegal states.
If what you really want is for your data to persist beyond an instance of your app, I suggest you look into using SharedPreferences or databases for more advanced data. You can then save your persistent data in onPause() or whenever it changes.
In an update to the accepted answer:
A fragment's onSaveInstanceState may be called if you are using a ViewPager with a FragmentStatePagerAdapter (rather than FragmentPagerAdapter)
FragmentStatePagerAdapter
This version of the pager is more useful when there are a large number of pages, working more like a list view. When pages are not visible to the user, their entire fragment may be destroyed, only keeping the saved state of that fragment. This allows the pager to hold on to much less memory associated with each visited page as compared to FragmentPagerAdapter at the cost of potentially more overhead when switching between pages.
And don't forget:
When using FragmentPagerAdapter the host ViewPager must have a valid ID set.
Not an accurate answer to the question, but may help someone's day.
In my case, I called
#Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
}
I replaced the above code as below and things worked
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
}

Duty of savedInstanceState in Android [duplicate]

I'm trying to save and restore the state of an Activity using the methods onSaveInstanceState() and onRestoreInstanceState().
The problem is that it never enters the onRestoreInstanceState() method. Can anyone explain to me why this is?
Usually you restore your state in onCreate(). It is possible to restore it in onRestoreInstanceState() as well, but not very common. (onRestoreInstanceState() is called after onStart(), whereas onCreate() is called before onStart().
Use the put methods to store values in onSaveInstanceState():
protected void onSaveInstanceState(Bundle icicle) {
super.onSaveInstanceState(icicle);
icicle.putLong("param", value);
}
And restore the values in onCreate():
public void onCreate(Bundle icicle) {
if (icicle != null){
value = icicle.getLong("param");
}
}
onRestoreInstanceState() is called only when recreating activity after it was killed by the OS. Such situation happen when:
orientation of the device changes (your activity is destroyed and recreated).
there is another activity in front of yours and at some point the OS kills your activity in order to free memory (for example). Next time when you start your activity onRestoreInstanceState() will be called.
In contrast: if you are in your activity and you hit Back button on the device, your activity is finish()ed (i.e. think of it as exiting desktop application) and next time you start your app it is started "fresh", i.e. without saved state because you intentionally exited it when you hit Back.
Other source of confusion is that when an app loses focus to another app onSaveInstanceState() is called but when you navigate back to your app onRestoreInstanceState() may not be called. This is the case described in the original question, i.e. if your activity was NOT killed during the period when other activity was in front onRestoreInstanceState() will NOT be called because your activity is pretty much "alive".
All in all, as stated in the documentation for onRestoreInstanceState():
Most implementations will simply use onCreate(Bundle) to restore their
state, but it is sometimes convenient to do it here after all of the
initialization has been done or to allow subclasses to decide whether
to use your default implementation. The default implementation of this
method performs a restore of any view state that had previously been
frozen by onSaveInstanceState(Bundle).
As I read it: There is no reason to override onRestoreInstanceState() unless you are subclassing Activity and it is expected that someone will subclass your subclass.
The state you save at onSaveInstanceState() is later available at onCreate() method invocation. So use onCreate (and its Bundle parameter) to restore state of your activity.
From the documentation Restore activity UI state using saved instance state it is stated as:
Instead of restoring the state during onCreate() you may choose to
implement onRestoreInstanceState(), which the system calls after the
onStart() method. The system calls onRestoreInstanceState() only if
there is a saved state to restore, so you do not need to check whether
the Bundle is null:
IMO, this is more clear way than checking this at onCreate, and better fits with single responsiblity principle.
As a workaround, you could store a bundle with the data you want to maintain in the Intent you use to start activity A.
Intent intent = new Intent(this, ActivityA.class);
intent.putExtra("bundle", theBundledData);
startActivity(intent);
Activity A would have to pass this back to Activity B. You would retrieve the intent in Activity B's onCreate method.
Intent intent = getIntent();
Bundle intentBundle;
if (intent != null)
intentBundle = intent.getBundleExtra("bundle");
// Do something with the data.
Another idea is to create a repository class to store activity state and have each of your activities reference that class (possible using a singleton structure.) Though, doing so is probably more trouble than it's worth.
The main thing is that if you don't store in onSaveInstanceState() then onRestoreInstanceState() will not be called. This is the main difference between restoreInstanceState() and onCreate(). Make sure you really store something. Most likely this is your problem.
I found that onSaveInstanceState is always called when another Activity comes to the foreground. And so is onStop.
However, onRestoreInstanceState was called only when onCreate and onStart were also called. And, onCreate and onStart were NOT always called.
So it seems like Android doesn't always delete the state information even if the Activity moves to the background. However, it calls the lifecycle methods to save state just to be safe. Thus, if the state is not deleted, then Android doesn't call the lifecycle methods to restore state as they are not needed.
Figure 2 describes this.
I think this thread was quite old. I just mention another case, that onSaveInstanceState() will also be called, is when you call Activity.moveTaskToBack(boolean nonRootActivity).
If you are handling activity's orientation changes with android:configChanges="orientation|screenSize" and onConfigurationChanged(Configuration newConfig), onRestoreInstanceState() will not be called.
It is not necessary that onRestoreInstanceState will always be called after onSaveInstanceState.
Note that :
onRestoreInstanceState will always be called, when activity is rotated (when orientation is not handled) or open your activity and then open other apps so that your activity instance is cleared from memory by OS.
In my case, onRestoreInstanceState was called when the activity was reconstructed after changing the device orientation. onCreate(Bundle) was called first, but the bundle didn't have the key/values I set with onSaveInstanceState(Bundle).
Right after, onRestoreInstanceState(Bundle) was called with a bundle that had the correct key/values.
I just ran into this and was noticing that the documentation had my answer:
"This function will never be called with a null state."
https://developer.android.com/reference/android/view/View.html#onRestoreInstanceState(android.os.Parcelable)
In my case, I was wondering why the onRestoreInstanceState wasn't being called on initial instantiation. This also means that if you don't store anything, it'll not be called when you go to reconstruct your view.
I can do like that (sorry it's c# not java but it's not a problem...) :
private int iValue = 1234567890;
function void MyTest()
{
Intent oIntent = new Intent (this, typeof(Camera2Activity));
Bundle oBundle = new Bundle();
oBundle.PutInt("MYVALUE", iValue); //=> 1234567890
oIntent.PutExtras (oBundle);
iRequestCode = 1111;
StartActivityForResult (oIntent, 1111);
}
AND IN YOUR ACTIVITY FOR RESULT
private int iValue = 0;
protected override void OnCreate(Bundle bundle)
{
Bundle oBundle = Intent.Extras;
if (oBundle != null)
{
iValue = oBundle.GetInt("MYVALUE", 0);
//=>1234567890
}
}
private void FinishActivity(bool bResult)
{
Intent oIntent = new Intent();
Bundle oBundle = new Bundle();
oBundle.PutInt("MYVALUE", iValue);//=>1234567890
oIntent.PutExtras(oBundle);
if (bResult)
{
SetResult (Result.Ok, oIntent);
}
else
SetResult(Result.Canceled, oIntent);
GC.Collect();
Finish();
}
FINALLY
protected override void OnActivityResult(int iRequestCode, Android.App.Result oResultCode, Intent oIntent)
{
base.OnActivityResult (iRequestCode, oResultCode, oIntent);
iValue = oIntent.Extras.GetInt("MYVALUE", -1); //=> 1234567890
}

getting exception "IllegalStateException: Can not perform this action after onSaveInstanceState"

I have a Live Android application, and from market i have received following stack trace and i have no idea why its happening as its not happening in application code but its getting caused by some or the other event from the application (assumption)
I am not using Fragments, still there is a reference of FragmentManager.
If any body can throw some light on some hidden facts to avoid this type of issue:
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyDown(Activity.java:1962)
at android.view.KeyEvent.dispatch(KeyEvent.java:2482)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1720)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1258)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1668)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2851)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2824)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2011)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4025)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599)
at dalvik.system.NativeStart.main(Native Method)
This is the most stupid bug I have encountered so far. I had a Fragment application working perfectly for API < 11, and Force Closing on API > 11.
I really couldn't figure out what they changed inside the Activity lifecycle in the call to saveInstance, but I here is how I solved this :
#Override
protected void onSaveInstanceState(Bundle outState) {
//No call for super(). Bug on API Level > 11.
}
I just do not make the call to .super() and everything works great. I hope this will save you some time.
EDIT: after some more research, this is a known bug in the support package.
If you need to save the instance, and add something to your outState Bundle you can use the following :
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
super.onSaveInstanceState(outState);
}
EDIT2: this may also occur if you are trying to perform a transaction after your Activity is gone in background. To avoid this you should use commitAllowingStateLoss()
EDIT3: The above solutions were fixing issues in the early support.v4 libraries from what I can remember. But if you still have issues with this you MUST also read #AlexLockwood 's blog : Fragment Transactions & Activity State Loss
Summary from the blog post (but I strongly recommend you to read it) :
NEVER commit() transactions after onPause() on pre-Honeycomb, and onStop() on post-Honeycomb
Be careful when committing transactions inside Activity lifecycle methods. Use onCreate(), onResumeFragments() and onPostResume()
Avoid performing transactions inside asynchronous callback methods
Use commitAllowingStateLoss() only as a last resort
Looking in Android source code on what causes this issue gives that flag mStateSaved in FragmentManagerImpl class (instance available in Activity) has value true. It is set to true when the back stack is saved (saveAllState) on call from Activity#onSaveInstanceState.
Afterwards the calls from ActivityThread don't reset this flag using available reset methods from FragmentManagerImpl#noteStateNotSaved() and dispatch().
The way I see it there are some available fixes, depending on what your app is doing and using:
Good ways
Before anything else: I would advertise Alex Lockwood article. Then, from what I've done so far:
For fragments and activities that don't need to keep any state information, call commitAllowStateLoss. Taken from documentation:
Allows the commit to be executed after an activity's state is saved. This is dangerous because the commit can be lost if the activity needs to later be restored from its state, so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user`. I guess this is alright to use if the fragment is showing read-only information. Or even if they do show editable info, use the callbacks methods to retain the edited info.
Just after the transaction is commit (you just called commit()), make a call to FragmentManager.executePendingTransactions().
Not recommended ways:
As Ovidiu Latcu mentioned above, don't call super.onSaveInstanceState(). But this means you will lose the whole state of your activity along with fragments state.
Override onBackPressed and in there call only finish(). This should be OK if you application doesn't use Fragments API; as in super.onBackPressed there is a call to FragmentManager#popBackStackImmediate().
If you are using both Fragments API and the state of your activity is important/vital, then you could try to call using reflection API FragmentManagerImpl#noteStateNotSaved(). But this is a hack, or one could say it's a workaround. I don't like it, but in my case it's quite acceptable since I have a code from a legacy app that uses deprecated code (TabActivity and implicitly LocalActivityManager).
Below is the code that uses reflection:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
invokeFragmentManagerNoteStateNotSaved();
}
#SuppressWarnings({ "rawtypes", "unchecked" })
private void invokeFragmentManagerNoteStateNotSaved() {
/**
* For post-Honeycomb devices
*/
if (Build.VERSION.SDK_INT < 11) {
return;
}
try {
Class cls = getClass();
do {
cls = cls.getSuperclass();
} while (!"Activity".equals(cls.getSimpleName()));
Field fragmentMgrField = cls.getDeclaredField("mFragments");
fragmentMgrField.setAccessible(true);
Object fragmentMgr = fragmentMgrField.get(this);
cls = fragmentMgr.getClass();
Method noteStateNotSavedMethod = cls.getDeclaredMethod("noteStateNotSaved", new Class[] {});
noteStateNotSavedMethod.invoke(fragmentMgr, new Object[] {});
Log.d("DLOutState", "Successful call for noteStateNotSaved!!!");
} catch (Exception ex) {
Log.e("DLOutState", "Exception on worka FM.noteStateNotSaved", ex);
}
}
Cheers!
Such an exception will occur if you try to perform a fragment transition after your fragment activity's onSaveInstanceState() gets called.
One reason this can happen, is if you leave an AsyncTask (or Thread) running when an activity gets stopped.
Any transitions after onSaveInstanceState() is called could potentially get lost if the system reclaims the activity for resources and recreates it later.
Simply call super.onPostResume() before showing your fragment or move your code in onPostResume() method after calling super.onPostResume(). This solve the problem!
This can also happen when calling dismiss() on a dialog fragment after the screen has been locked\blanked and the Activity + dialog's instance state has been saved. To get around this call:
dismissAllowingStateLoss()
Literally every single time I'm dismissing a dialog i don't care about it's state anymore anyway, so this is ok to do - you're not actually losing any state.
Short And working Solution :
Follow Simple Steps :
Step 1 : Override onSaveInstanceState state in respective fragment. And remove super method from it.
#Override
public void onSaveInstanceState(Bundle outState) {
};
Step 2 : Use CommitAllowingStateLoss(); instead of commit(); while fragment operations.
fragmentTransaction.commitAllowingStateLoss();
I think Lifecycle state can help to prevent such crash starting from Android support lib v26.1.0 you can have the following check:
if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)){
// Do fragment's transaction commit
}
or you can try:
Fragment.isStateSaved()
more information here
https://developer.android.com/reference/android/support/v4/app/Fragment.html#isStateSaved()
this worked for me... found this out on my own... hope it helps you!
1) do NOT have a global "static" FragmentManager / FragmentTransaction.
2) onCreate, ALWAYS initialize the FragmentManager again!
sample below :-
public abstract class FragmentController extends AnotherActivity{
protected FragmentManager fragmentManager;
protected FragmentTransaction fragmentTransaction;
protected Bundle mSavedInstanceState;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSavedInstanceState = savedInstanceState;
setDefaultFragments();
}
protected void setDefaultFragments() {
fragmentManager = getSupportFragmentManager();
//check if on orientation change.. do not re-add fragments!
if(mSavedInstanceState == null) {
//instantiate the fragment manager
fragmentTransaction = fragmentManager.beginTransaction();
//the navigation fragments
NavigationFragment navFrag = new NavigationFragment();
ToolbarFragment toolFrag = new ToolbarFragment();
fragmentTransaction.add(R.id.NavLayout, navFrag, "NavFrag");
fragmentTransaction.add(R.id.ToolbarLayout, toolFrag, "ToolFrag");
fragmentTransaction.commitAllowingStateLoss();
//add own fragment to the nav (abstract method)
setOwnFragment();
}
}
I was always getting this when I tried to show fragment in onActivityForResult() method, so the problem was next:
My Activity is paused and stopped, which means, that onSaveInstanceState() was called already (for both pre-Honeycomb and post-Honeycomb devices).
In case of any result I made transaction to show/hide fragment, which causes this IllegalStateException.
What I made is next:
Added value for determining if action I want was done (e.g. taking photo from camere - isPhotoTaken) - it can be boolean or integer value depending how much different transactions you need.
In overriden onResumeFragments() method I checked for my value and after made fragment transactions I needed. In this case commit() was not done after onSaveInstanceState, as state was returned in onResumeFragments() method.
I solved the issue with onconfigurationchanged. The trick is that according to android activity life cycle, when you explicitly called an intent(camera intent, or any other one); the activity is paused and onsavedInstance is called in that case. When rotating the device to a different position other than the one during which the activity was active; doing fragment operations such as fragment commit causes Illegal state exception. There are lots of complains about it. It's something about android activity lifecycle management and proper method calls.
To solve it I did this:
1-Override the onsavedInstance method of your activity, and determine the current screen orientation(portrait or landscape) then set your screen orientation to it before your activity is paused. that way the activity you lock the screen rotation for your activity in case it has been rotated by another one.
2-then , override onresume method of activity, and set your orientation mode now to sensor so that after onsaved method is called it will call one more time onconfiguration to deal with the rotation properly.
You can copy/paste this code into your activity to deal with it:
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Toast.makeText(this, "Activity OnResume(): Lock Screen Orientation ", Toast.LENGTH_LONG).show();
int orientation =this.getDisplayOrientation();
//Lock the screen orientation to the current display orientation : Landscape or Potrait
this.setRequestedOrientation(orientation);
}
//A method found in stackOverflow, don't remember the author, to determine the right screen orientation independently of the phone or tablet device
public int getDisplayOrientation() {
Display getOrient = getWindowManager().getDefaultDisplay();
int orientation = getOrient.getOrientation();
// Sometimes you may get undefined orientation Value is 0
// simple logic solves the problem compare the screen
// X,Y Co-ordinates and determine the Orientation in such cases
if (orientation == Configuration.ORIENTATION_UNDEFINED) {
Configuration config = getResources().getConfiguration();
orientation = config.orientation;
if (orientation == Configuration.ORIENTATION_UNDEFINED) {
// if height and widht of screen are equal then
// it is square orientation
if (getOrient.getWidth() == getOrient.getHeight()) {
orientation = Configuration.ORIENTATION_SQUARE;
} else { //if widht is less than height than it is portrait
if (getOrient.getWidth() < getOrient.getHeight()) {
orientation = Configuration.ORIENTATION_PORTRAIT;
} else { // if it is not any of the above it will defineitly be landscape
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
}
}
}
return orientation; // return value 1 is portrait and 2 is Landscape Mode
}
#Override
public void onResume() {
super.onResume();
Toast.makeText(this, "Activity OnResume(): Unlock Screen Orientation ", Toast.LENGTH_LONG).show();
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);
}
I had the same problem, getting IllegalStateException, but replacing all my calls to commit() with commitAllowingStateLoss() did not help.
The culprit was a call to DialogFragment.show().
I surround it with
try {
dialog.show(transaction, "blah blah");
}
catch(IllegalStateException e) {
return;
}
and that did it. OK, I don't get to show the dialog, but in this case that was fine.
It was the only place in my app where I first called FragmentManager.beginTransaction() but never called commit() so I did not find it when I looked for "commit()".
The funny thing is, the user never leaves the app. Instead the killer was an AdMob interstitial ad showing up.
My solution for that problem was
In fragment add methods:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
...
guideMapFragment = (SupportMapFragment)a.getSupportFragmentManager().findFragmentById(R.id.guideMap);
guideMap = guideMapFragment.getMap();
...
}
#Override
public void onDestroyView() {
SherlockFragmentActivity a = getSherlockActivity();
if (a != null && guideMapFragment != null) {
try {
Log.i(LOGTAG, "Removing map fragment");
a.getSupportFragmentManager().beginTransaction().remove(guideMapFragment).commit();
guideMapFragment = null;
} catch(IllegalStateException e) {
Log.i(LOGTAG, "IllegalStateException on exit");
}
}
super.onDestroyView();
}
May be bad, but couldn't find anything better.
I got this issue.But I think this problem is not related to commit and commitAllowStateLoss.
The following stack trace and exception message is about commit().
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1341)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1352)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
But this exception was caused by onBackPressed()
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(Unknown Source)
at android.support.v4.app.FragmentManagerImpl.popBackStackImmediate(Unknown Source)
at android.support.v4.app.FragmentActivity.onBackPressed(Unknown Source)
They were all caused by checkStateLoss()
private void checkStateLoss() {
if (mStateSaved) {
throw new IllegalStateException(
"Can not perform this action after onSaveInstanceState");
}
if (mNoTransactionsBecause != null) {
throw new IllegalStateException(
"Can not perform this action inside of " + mNoTransactionsBecause);
}
mStateSaved will be true after onSaveInstanceState.
This problem rarely happens.I have never encountered this problem.I can not reoccurrence the problem.
I found issue 25517
It might have occurred in the following circumstances
Back key is called after onSaveInstanceState, but before the new activity is started.
use onStop() in code
I'm not sure what the root of the problem is.
So I used an ugly way.
#Override
public void onBackPressed() {
try{
super.onBackPressed();
}catch (IllegalStateException e){
// can output some information here
finish();
}
}
I have got the same issue in my App. I have been solved this issue just calling the super.onBackPressed(); on previous class and calling the commitAllowingStateLoss() on the current class with that fragment.
onSaveInstance will be called if a user rotates the screen so that it can load resources associated with the new orientation.
It's possible that this user rotated the screen followed by pressing the back button (because it's also possible that this user fumbled their phone while using your app)
Another lifecycle way to solve the issue is to use the latest released lifecycle-ktx with kotlin.
lifecycleScope.launchWhenResumed {
// your code with fragment or dialogfragment
}
The closure will be run after resume state, so even this method is called after
stop, it'll be safly excuted when the next resume come.
You can also choose like
lifecycleScope.launchWhenCreated
// or
lifecycleScope.launchWhenStarted
to fit your situation.
The code will be cancelled when destroy is come.
The Google document link:
https://developer.android.com/kotlin/ktx#lifecycle
Read
http://chris-alexander.co.uk/on-engineering/dev/android-fragments-within-fragments/
article.
fragment.isResumed() checking helps me in onDestroyView w/o using onSaveInstanceState method.
Same issue from me and after a day long analysis of all articles, blog and stackoverflow i've found a simple solution. Don't use savedInstanceState at all, this is the condition with one line of code. On the fragment code:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
.....
This happens whenever you are trying to load a fragment but the activity has changed its state to onPause().This happens for example when you try to fetch data and load it to the activity but by the time the user has clicked some button and has moved to next activity.
You can solve this in two ways
You can use transaction.commitAllowingStateLoss() instead of transaction.commit() to load fragment but you may end up losing commit operation that is done.
or
Make sure that activity is in resume and not going to pause state when loading a fragment.
Create a boolean and check if activity is not going to onPause() state.
#Override
public void onResume() {
super.onResume();
mIsResumed = true;
}
#Override
public void onPause() {
mIsResumed = false;
super.onPause();
}
then while loading fragment check if activity is present and load only when activity is foreground.
if(mIsResumed){
//load the fragment
}
Thanks #gunar, but I think there is a better way.
According to doc :
* If you are committing a single transaction that does not modify the
* fragment back stack, strongly consider using
* {#link FragmentTransaction#commitNow()} instead. This can help avoid
* unwanted side effects when other code in your app has pending committed
* transactions that expect different timing.
*
* #return Returns true if there were any pending transactions to be
* executed.
*/
public abstract boolean executePendingTransactions();
So use commitNow to replace:
fragmentTransaction.commit();
FragmentManager.executePendingTransactions()
Well, after trying all the above solutions without success (because basically i dont have transactions).
On my case i was using AlertDialogs and ProgressDialog as fragments that, sometimes, on rotation, when asking for the FragmentManager, the error rises.
I found a workaround mixing some many similar posts:
Its a 3 step solution, all done on your FragmentActivity (in this case, its called GenericActivity):
private static WeakReference<GenericActivity> activity = null; //To avoid bug for fragments: Step 1 of 3
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
//To avoid bug for fragments: Step 2 of 3
activity = new WeakReference<GenericActivity>(this);
}
#Override
public FragmentManager getSupportFragmentManager(){
//To avoid bug for fragments: Step 3 of 3
if (this == activity.get()) {
return super.getSupportFragmentManager();
}
return activity.get().getSupportFragmentManager();
}
When i use startactivity in one fragment, i will get this exception;
When i change to use startactivityforresult, the exception is gone :)
So the easy way to fix it is use the startActivityForResult api :)
I was getting this exception when I was pressing back button to cancel intent chooser on my map fragment activity.
I resolved this by replacing the code of onResume()(where I was initializing the fragment and committing transaction) to onStart() and the app is working fine now.
Hope it helps.
This is fixed in Android 4.2 and also in the support library's source.[*]
For details of the cause (and work-arounds) refer to the the Google bug report:
http://code.google.com/p/android/issues/detail?id=19917
If you're using the support library then you shouldn't have to worry about this bug (for long)[*]. However, if you're using the API directly (i.e. Not using the support library's FragmentManager) and targeting an API below Android 4.2 then you will need to try one of the work-arounds.
[*] At the time of writing the Android SDK Manager is still distributing an old version that exhibits this bug.
Edit I'm going to add some clarification here because I've obviously somehow confused whoever down-voted this answer.
There are several different (but related) circumstances that can cause this exception to be thrown. My answer above is referring to the specific instance discussed in the question i.e. a bug in Android which has subsequently been fixed. If you're getting this exception for another reason it's because you're adding/removing fragments when you shouldn't be (after fragment states have been saved). If you're in such a situation then perhaps "Nested Fragments - IllegalStateException “Can not perform this action after onSaveInstanceState”" can be of use to you.
After researching a bit the solution to this problem is to do your fragment commits in the onresume.
Source: https://wenchaojames.wordpress.com/2013/01/12/illegalstateexception-from-onactivityresult/
My use case: I have used listener in fragment to notify activity that some thing happened. I did new fragment commit on callback method. This works perfectly fine on first time. But on orientation change the activity is recreated with saved instance state. In that case fragment is not created again implies that the fragment have the listener which is old destroyed activity. Any way the call back method will get triggered on action. It goes to destroyed activity which cause the issue. The solution is to reset the listener in fragment with current live activity. This solve the problem.
What I found is that if another app is dialog type and allows touches to be sent to background app then almost any background app will crash with this error.
I think we need to check every time a transaction is performed if the instance was saved or restored.
In my case, with the same error exception, i put the "onBackPressed()" in a runnable (you can use any of your view):
myView.post(new Runnable() {
#Override
public void run() {
onBackPressed()
}
});
I do not understand why, but it works!
You may be calling fragmentManager.popBackStackImmediate(); when activity is paused. Activity is not finished but is paused and not on foreground. You need to check whether activity is paused or not before popBackStackImmediate().
I noticed something very interesting. I have in my app the option to open the phone's gallery and the device asks what app to use, there I click on the gray area away from the dialog and saw this issue. I noticed how my activity goes from onPause, onSaveInstanceState back to onResume, it doesn't happen to visit onCreateView. I am doing transactions at onResume. So what I ended up doing is setting a flag being negated onPause, but being true onCreateView. if the flag is true onResume then do onCommit, otherwise commitAllowingStateLoss. I could go on and waste so much time but I wanted to check the lifecycle. I have a device which is sdkversion 23, and I don't get this issue, but I have another one which is 21, and there I see it.

Categories

Resources