Fragments are recreated automatically when their parent Activity or Fragment is recreated. If the child fragments were related to state that is not retained between instances of their parent, when should I remove them?
In the parent fragment's onDestroy(): unreliable since onDestroy() might not be called.
In the parent fragment's onCreate(): presumably the children have not yet been created at this point.
Some other lifecycle method that is guaranteed to be called after the children have been recreated and added. Is onViewStateRestored(...) the right place for this?
In case my question isn't clear, here's an example:
An Activity has a Fragment which contains an asynchronous operation. The fragment would normally cancel this task in onDestroy(). But if the fragment is destroyed without onDestroy() being called, it may later find itself recreated with the background task uninitialized. In that case, it should remove its old progress dialog. When should it test for this condition?
Edit: When the user swipes the app out of recents, all its components are destroyed without calls to onDestroy(). But in that case, the fragment hierarchy is apparently obliterated along with the rest of the app. When the app is restarted, the fragment is not automatically recreated, so I don't have to worry about removing it.
When the fragment is destroyed because its host activity is put in the background and "don't keep activities" is turned on, the fragment is automatically recreated. But in that case, it seems I can count on onDestroy() being called.
My concern is what happens when the app is killed to free memory. Hopefully it will behave like swiping from recents, where the fragment hierarchy is not restored. That would render my whole question moot. Can anyone confirm what happens in that case?
The docs seem to indicate onpause will always be called
https://developer.android.com/guide/components/fragments.html
or if you know the activity is going to be destroyed onDetach() must be called since if the activity is destroyed the fragment can't be attached.
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 have three activities in my application.
From the first one i started the second, and from the second i started the third. When the process of my app is killed by the system and i launch it again i see only the last active activity is being created. And it seems the only one that gets Bundle object as a parameter to its onCreate method.
There are two things I am curious of:
If the activity at the top of the stack is the only one that gets its bundle, why each of my activities got their callback (onSaveInstanceState) called just before their onStop() method.
If only one of my activities can retain original state, what about the others? Did they lose all their state just because system decided to kill my app process? Should i restore them manually? What about views on them? (normally views get their state back without needing me to put something into the bundle and later restore if i am not mistaken)
The default behavior is this (tested on 4.1.1):
When you start a new activity, onSaveInstanceState of the previous activity is being called
When the system kills the app, the state of activities back-stack is being saved
When you restart the app, the last seen activity is being shown and its onRestoreInstanceState is being called
When you navigate back and pop activities from the back stack, state of each of them will be restored with a call to onRestoreInstanceState passing the bundle that was obtained from the first step above.
Therefore, the answers to your questions are:
All activities will be restored, but not at once - the last one seen is restored immediately, while the others will be restored when you navigate back.
All simple views (e.g. EditText) will automatically restore their state. In order for this to happen, you need to make sure that a) you did not override onSaveInstanceState or onRestoreInstanceState without call to super implementations b) the views that should be restored have unique IDs in view hierarchy
onStop is called on previous activity when you create a new activity, so you are saving the state! Your previous activity's onCreate will be called, if it's destroyed, so don't worry about recreating views. Are you sure app is killed? An app is not killed when it loses view. Android will keep it in memory as long as it doesn't need to free memory.
Is there a way to find out if a fragment is being permanently destroyed, i.e. that it won't be recreated in the future?
I need this to know when should I destroy a network task associated with the fragment.
Something that should work is to call getActivity().isChangingConfigurations() in the fragment's onDestroy().
This will all depend on how you have created and set up you fragment(s). An easy way to check to see if your fragment(s) is being permanently destroyed is to add a toast or some other sort of notification/flag that you can check in the onDestroy() method of the fragment. If you see the toast of if the flag is set then you know that the fragment was destroyed.
In your fragment, override onDestroy and cancel your AsyncTasks or other operations then
AsyncTask task;
#Override
public void onDestroy() {
if (task != null && !task.isCancelled()){
task.cancel(true);
task = null;
}
super.onDestroy();
}
If the task is associated with a fragment, it should be destroyed in the fragment's onDestroy method. The system assumes that all of the fragment's resources have been cleaned up once that method returns, so you should just go ahead and do the required clean-up.
If the fragment is re-created in the future, it will be a whole new fragment so will presumably require a whole new network task anyway.
EDIT:
If you want work to continue in the background when an activity is destroyed and re-created due to a configuration change (such as a screen rotation), you can set up your fragment to not be destroyed. You need to do two relatively simple things:
Define your fragment to have no UI (so that it has nothing that needs to be cleaned up when the activity is destroyed).
Call setRetainInstance(true) for the fragment to prevent the fragment from being destroyed along with the activity. When the activity is re-created after a configuration change, the retained fragment will be reattached. Meanwhile it can continue the background processing.
I want to use setRetainInstance(true) on my FragmentActivity so that onCreate() will not be called every time the screen rotates. I just want to adjust the layout to the screen adjusting without reestablishing my location services connection and notifying the user. How should this be done?
Start by reading the Fragment documentation about what setRetainInstance does (And does not).
In summary:
public void setRetainInstance (boolean retain)
Added in API level 11 Control whether a fragment instance is retained
across Activity re-creation (such as from a configuration change).
This can only be used with fragments not in the back stack. If set,
the fragment lifecycle will be slightly different when an activity is
recreated:
onDestroy() will not be called (but onDetach() still will be, because
the fragment is being detached from its current activity).
onCreate(Bundle) will not be called since the fragment is not being
re-created. onAttach(Activity) and onActivityCreated(Bundle) will
still be called.
With that in mind, make sure your "location services connection" and such are located in a place where they will not die (they probably shouldn't be in either Activity or Fragment anyway).
This paragraph is taken from a book I'm currently reading "Professional Android 4 Application Development"
If an Activity is destroyed and restarted to handle a hardware
configuration change, such as the screen orientation changing, you can
request that your Fragment instance be retained. By calling
setRetainInstance within a Fragment’s onCreate handler, you specify
that Fragment’s instance should not be killed and restarted when its
associated Activity is re-created.
I think that's clear enough.
Regards!
I have a singleton in an activity. When I end my application (like pressing back button), and start it again after some time, the singleton is not recreated, but is holding previous state. Singleton is not destroyed if the application is destroyed? Do I have to null its static members in onDestroy() to avoid memory leak?
Thanks.
When you are 'ending' your activity it is just going in background. So state will be maintained. If you want to do something when the activity is not 'visible' you have to implement the onStop() method and not onDestroy().
Please refer how android manages stack of Tasks and activities. Also refer Activity Lifecycle.
Activity is stopped if it is
completely obscured by another
activity. It still retains all state
and member information. However, it is
no longer visible to the user so its
window is hidden and it will often be
killed by the system when memory is
needed elsewhere.