I have read a lot of similar questions, but still couldn't find the answer.
I have an app deployed to the market, and I receive some IllegalStateException crash reports through BugSnag. But I can't reproduce it on my end, all the information I have for this problem is from BugSnag.
The stack trace for my problem is
java.lang.IllegalStateExceptionSplashScreenActivity
Can not perform this action after onSaveInstanceState
java.lang.IllegalStateException ยท Can not perform this action after onSaveInstanceState
FragmentManager.java:2044android.support.v4.app.FragmentManagerImpl.checkStateLoss
FragmentManager.java:2067android.support.v4.app.FragmentManagerImpl.enqueueAction
BackStackRecord.java:680android.support.v4.app.BackStackRecord.commitInternal
BackStackRecord.java:634android.support.v4.app.BackStackRecord.commit
FragmentTabHost.java:288android.support.v4.app.FragmentTabHost.onAttachedToWindow
View.java:20011android.view.View.dispatchAttachedToWindow
ViewGroup.java:3589android.view.ViewGroup.dispatchAttachedToWindow
ViewGroup.java:3596android.view.ViewGroup.dispatchAttachedToWindow
ViewGroup.java:3596android.view.ViewGroup.dispatchAttachedToWindow
ViewGroup.java:3596android.view.ViewGroup.dispatchAttachedToWindow
ViewGroup.java:3596android.view.ViewGroup.dispatchAttachedToWindow
ViewGroup.java:3596android.view.ViewGroup.dispatchAttachedToWindow
ViewGroup.java:3596android.view.ViewGroup.dispatchAttachedToWindow
ViewRootImpl.java:2262android.view.ViewRootImpl.performTraversals
ViewRootImpl.java:1927android.view.ViewRootImpl.doTraversal
ViewRootImpl.java:8558android.view.ViewRootImpl$TraversalRunnable.run
Choreographer.java:949android.view.Choreographer$CallbackRecord.run
Choreographer.java:761android.view.Choreographer.doCallbacks
Choreographer.java:696android.view.Choreographer.doFrame
Choreographer.java:935android.view.Choreographer$FrameDisplayEventReceiver.run
Handler.java:873android.os.Handler.handleCallback
Handler.java:99android.os.Handler.dispatchMessage
Looper.java:214android.os.Looper.loop
ActivityThread.java:7124android.app.ActivityThread.main
Method.java:-2java.lang.reflect.Method.invoke
RuntimeInit.java:494com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run
ZygoteInit.java:975com.android.internal.os.ZygoteInit.main
The following is my program logic. The FragmentTabHost is only used in HomeActivity. Its setup is done entirely in onCreate, and it is not touched in other lifecycle callbacks.
From the bugsnag, I get the following breadcrumb. From the breadcrumb I can see the exception happens when the user press home_key or recent_app right after launch and in early phase of HomeActivity.
My questions are:
Why the Exception is registered under the SplashScreenActivity, shouldn't that activity finish long before the HomeActivity?
I see that TabHost tries to commit fragment transaction after onSaveInstanceState(), but any methods that involve committing fragments are executed in onCreate, why those commit events are scheduled after onPause?
Related
I have done ample research on this, and there is not one clear solution on the problem.
In the life-cycle, particularly in the Fragment life-cycle, following Exception comes any moment after onPause().
java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
My logic says, that to continue with the current fragment, after it reaches this state, I have to restart the activity and again point back to the intended fragment using Intent.
I want to be clear on what is happening and what should be real solution to deal with it.
I need to know the pros and cons of this mechanism; its importance in Fragment or Activity life-cycle.
Also, if I am changing the Windows Feature in onCreate to not to go to sleep, unless if the user has manually pressed the home button, will still the activity will go to this state?
This exception happens when you're trying to add/remove/replace/interact in any other way with a Fragment inside the Activity when it's paused.
Which means Activity will not be able to restore it's state (restore the state of a Fragment which has been changed) if it will be destroyed right away.
Best solution here, is to check that Activity is NOT paused during the interaction with a Fragment.
Another option is to use commitAllowingStateLoss() to interact with Fragment transaction, with a risk of losing it's state.
See:
https://developer.android.com/reference/android/app/FragmentTransaction.html#commitAllowingStateLoss()
In a perfect world you should analyze each crash carefully and add checks to verify that you interact with fragments only when Activity is up and running.
A better explanation is presented in a new Android developer reference and guide documents for using JetPack Life Cycle Listener.
https://developer.android.com/topic/libraries/architecture/lifecycle#kotlin
The library makes the components Activity Life Cycle aware. That means you do not require an abstract baseActivity class which overrides every life cycle callback, and record that state in a boolean variable. LifeCycle listener will do it for you.
All you have to do is stop introducing a new fragment or stop any Loader that updates the UI when its response returns. The right time to do this is before onStop or onSavedInstance state is called, and your components will be made aware of it.
It clearly states that after the onSavedInstancState or onStop is called the UI becomes immutable till the onStart of the Activity is called again. Sometimes you have to call restart the same activity using NEW TASK and CLEAR TASK flags using intent, when this state occurs and there is no chance that otherwise onStart is going to be called.
Happy Coding :-)
I am sending an HTTP request to some server(in a background thread) inside onCreateView() method within my fragment class.
When the request received i check some stuff and according to that i add a new fragment(through fragment transaction in the UI thread).
I use the support library
But, for example, if the user press the android home button, while the request hasn't received yet, the fragment is going to pause or stop state and then the request received and of course the exception is thrown(just after trying to commit).
I searched the web and found some very good articles and answers which are relevant to this issue and all the things related to the 'state loss' after onSaveInstanceState() has been called, etc...
For example i read this excellent article of Alex Lockwood: http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html
and also those stackoverflow questions/answers: getting exception "IllegalStateException: Can not perform this action after onSaveInstanceState"
and: "Can not perform this action after onSaveInstanceState" - why am I getting this exception from my activity's onResume method? and more.
But i couldn't came to a conclusion of what to do in my case.
The only thing that came to mind is to use commitAllowingStateLoss() instead of commit() but this fills hacky and not correct as some of the answers that i read conclude about it.
Also there was a suggestion to commit the transaction in onCreate() because it safe, but of course it can't work in my situation, all other suggestions doesn't work for me as well.
Maybe i can use some boolean flag to check if i can't make a transaction and then wait for the fragment to resume and if the flag is true then do the transaction.
This fills to much work and also presents the problem in which i need to know if the transaction can be done(don't even know how to check it).
I have a flow in my android app where it is possible to open a chain of user profile activities, one activity from another.
Example : User profile A is opened where it contains a list of other user profiles. clicking an item from this list will open user profile B. Again, it lists other user profiles where user profile A might be part of. So clicking this item will open another activity of user profile A.
The app user can perform an action on the current activity of user profile A which needs to be reflected on all other user profile A activities in the back stack. So what i did was registered a receiver in the user profile activity that checks for the activity user id against the one coming from the broadcast and perform the relevant actions on the UI.
The problem is, that i cannot unreigster the receiver on onPause() or onStop() (according to a lot of threads recommendations here) since this is kindda counterproductive to what im trying to build here. And according to the documentation onDestroy() is not guarunteed to be called every time the activity terminates.
So what im basically asking here is - Is it a good practice to register all activity receivers on onCreate() and unregister them both on onDestroy() and onSaveInstanceState() so i will be 100% sure they are cleaned up on activity destruction ?
The only mention i saw in the documentation that recomments not to unregister receivers in onSaveInstanceState() was here - BroadcastReceiver - and it only says
Do not unregister in Activity.onSaveInstanceState(), because this won't be called if the user moves back in the history stack
EDIT:
Ok, i just saw this quote in the onDestroy() spec :
There are situations where the system will simply kill the activity's hosting process without calling this method (or any others) in it, so it should not be used to do things that are intended to remain around after the process goes away.
So onSaveInstanceState() will not be called also.
But arent receivers qualified as things that are NOT "intended to remain around after the process goes away" ? I dont get why onDestroy() is not called in such situations. What happens to other resources that are released there (not just receivers) ?
In your case, using broadcast receiver to detect which Activity instance is running is not a good idea. You should use single instance for every Activity. Another solution is that you can create view stack in one Activity instead of Activity stack. Switching view is easier than switching Activity. As to how to use onDestroy and onSaveInstanceState(), it depends on your scenario. If you finish your Activity, the onDestroy will be called. If your Activity is destroyed by system in some situation, like screen rotation, the onSaveInstanceState will be triggered.
I have an activity that is triggered by a notification (when the user clicks).
1) When there is no other activity (from my app) alive, the activity does not rotate when the orientation changes. OnCreate() does not get called
2) When another activity is running (below), then when I rotate the device onCreate() gets called once but the activity dies immediately. No error logged
I can't see anything special in my activity but the fact that it is triggered by a notification and that it has a broadcast receiver in it (private class).
Thank you for your thoughts
EDIT: Please discard this question. I had a finish() in onStop().
Add a log statement in the Activity.onCreate() method that is dying. More likely is your activity is being recreated and something is tossing an exception. If you see the onCreate() statement in the logs twice then you know you likely having an exception being thrown. Even without it you can trace down which line its dying on by adding more log statements.
On orientation changes you're activity will be torn down and rebuilt unless you fixed the orientation in the manifest. If a service or broadcast receiver has a reference to it after it's torn down then any call to it will likely cause it to die as you're seeing. This is where Android's lifecycle is more hassle than its worth, but it is what it is.
More than likely something is throwing an exception and you're not seeing it or its being masked by another one being thrown. Make sure you're not filtering your logcat output by tag.
When I run my app on the debugger, I get the main thread and 3 binder threads.
On a button click I call Activity.finish(), which looks like it ends the activity as the UI closes and goes back to the home screen.
However, in the debugger, it still shows the main thread and 3 binder threads as "(running)".
I am baffled to why this is happening. Even more so, it is causing my app to call Activity.onResume() when I run it again after exiting the app.
I currently override these methods in the Activity, but I call the appropriate super functions in each one:
onDestroy()
onPause()
onResume()
onSaveInstanceState()
Any help or advice regarding this is much appreciated!
You don't control when your app leaves main memory, the OS does. Look closely at Activity.finish...
Call this when your activity is done
and should be closed. The
ActivityResult is propagated back to
whoever launched you via
onActivityResult().
Note that is says nothing about memory. As to calling Activity.onResume, that's exactly what you would expect for the lifecycle; remeber that onResume is not just called after a resume but even when the app is first launched after onCreate.
While not exactly what you asked I suggest you read this article about exit buttons which goes on to say something very important
[Activity.finish] is exactly equivalent to hitting the back button.