i have a fragment that is hosted inside of an activity. when user prsses the back button i need to save the model data and have it available the next time user opens the fragment/activity. But just while in the app, it does not need to be persisted to disk. So for example if user destroyed the process, then there is no need to keep the model data, it can be fetched from network again.
what i have tried:
icePick and onSavedInstance calls but these dont seem to kick in when user presses the back button on the fragment. tell me if im wrong.
here is what i have implemented in my fragment:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelable("myModel", Parcels.wrap(myModel));
}
i am using the parceler library if that makes any difference. I can also convert the code to kotlin if required. when i hit the back button the fragment gets popped off the stack and the activity contain it calls onDestroy but im not getting any call back in onSaveInstanceState. Also when i check in onCreate() savedInstanceState is null. I have not overrided onSavedInstance in the activity, just in the fragment. What am i doing wrong ?
i had a though to use a database to do this, but i just need it while in memory and there should be a way to do this without a DB.
from what i learned if user hits the back button onSaveInstance is not called by the system:
If an activity is in the foreground, and the user taps the Back button, the activity transitions through the onPause(), onStop(), and onDestroy() callbacks. In addition to being destroyed, the activity is also removed from the back stack.
It is important to note that, by default, the onSaveInstanceState()
callback does not fire in this case.
source: here
#onSaveInstanceState of fragment is strictly coupled to activity lifecycle
According to doc
Called to retrieve per-instance state from an activity before being
killed
You operates only with fragments and activity is left untouched,
so this method is definitely can't be used in your case and shouldn't.
My suggestion is to use some kind of persistent storage though interface. It could be in memory storage (any type of singleton, like suggested in comments. It may be scoped to app or activity or to custom case (you have to control manually cache lifecycle) and injected with dagger, for example), shared-preferences based storage, database storage. It is easy to test if you follow dependency injection patterns & use structural pattern like MVP (but it is not a point of this question)
So store the data in the repository on change or in the onPause method (because it is the last guaranteed to call when screen is being gone). And restore it in onCreate
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 :-)
When orientation of screen changes, I have read many a times that in order to save data of edit text and text view or any of the radio button I have to use onSaveInstanceState() method.
But when I'm changing the screen orientation my data of edit text, text view and radio button are not getting erased.
So what is the main purpose of using onSaveInstanceState() method. Why do we have to use it if my data are preserved safely ?
Some Views/properties may be handled by default. You could go rooting through the docs to find out exactly which ones and how they are taken care of but...
I would recommend that you take manual control of these save/loads though to ensure that things are handled as you want them to be to avoid edge case bugs and also so you can then persist certain settings/properties/states if need be.
It really depends on the complexity and contents of your Activity/Fragment/Layout/View/Preference etc etc and if you even really need to remember the state things were a few moments ago.
onSaveInstanceState(Bundle outState)
This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state.
Do not confuse this method with activity lifecycle callbacks such as onPause, which is always called when an activity is being placed in the background or on its way to destruction, or onStop which is called before destruction. One example of when onPause and onStop is called and not this method is when a user navigates back from activity B to activity A: there is no need to call onSaveInstanceState on B because that particular instance will never be restored, so the system avoids calling it. An example when onPause is called and not onSaveInstanceState is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact.
onRestoreInstanceState(Bundle savedInstanceState)
This method is called after onStart() when the activity is being re-initialized from a previously saved state.
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).
On the default handling of your views with this pair of methods:
The default implementation takes care of most of the UI per-instance state for you by calling View.onSaveInstanceState() on each view in the hierarchy that has an id, and by saving the id of the currently focused view (all of which is restored by the default implementation of onRestoreInstanceState). If you override this method to save additional information not captured by each individual view, you will likely want to call through to the default implementation, otherwise be prepared to save all of the state of each view yourself.
When OS will completely forget the state about an Activity and all information relate to an Activity ( any records about an Activity) ?
In other words when it will make Bundle to be a new instance?
I found the below explanation but it does not explain this "Killing bundle" point?
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/Activity.java#Activity.onSaveInstanceState%28android.os.Bundle%29
Called to retrieve per-instance state from an activity before being killed so that the state can be restored in onCreate(android.os.Bundle) or onRestoreInstanceState(android.os.Bundle) (the android.os.Bundle populated by this method will be passed to both).
This method is called before an activity may be killed so that when it comes back some time in the future it can restore its state. For example, if activity B is launched in front of activity A, and at some point activity A is killed to reclaim resources, activity A will have a chance to save the current state of its user interface via this method so that when the user returns to activity A, the state of the user interface can be restored via onCreate(android.os.Bundle) or onRestoreInstanceState(android.os.Bundle).
Do not confuse this method with activity lifecycle callbacks such as onPause(), which is always called when an activity is being placed in the background or on its way to destruction, or onStop() which is called before destruction. One example of when onPause() and onStop() is called and not this method is when a user navigates back from activity B to activity A: there is no need to call onSaveInstanceState(android.os.Bundle) on B because that particular instance will never be restored, so the system avoids calling it. An example when onPause() is called and not onSaveInstanceState(android.os.Bundle) is when activity B is launched in front of activity A: the system may avoid calling onSaveInstanceState(android.os.Bundle) on activity A if it isn't killed during the lifetime of B since the state of the user interface of A will stay intact.
The default implementation takes care of most of the UI per-instance state for you by calling android.view.View.onSaveInstanceState() on each view in the hierarchy that has an id, and by saving the id of the currently focused view (all of which is restored by the default implementation of onRestoreInstanceState(android.os.Bundle)). If you override this method to save additional information not captured by each individual view, you will likely want to call through to the default implementation, otherwise be prepared to save all of the state of each view yourself.
If called, this method will occur before onStop(). There are no guarantees about whether it will occur before or after onPause().
There are many cases where this could occur:
Here is a small topic on reference mentioning different
use-cases about recreating activities.
If you want more control over the application life cycle:
Take a look a the application object for more information.
Personal advice:
I think you should not care about when/what, This is something the
android system will manage for you, your code should be optimized to
handle all usecases (when the bundle will be null or not). A bundle
should only be used to handle state-keeping, if you want to
persist data you should take a look at fileI/O, SQLlite or shared
preference.
Quick question.
I have an activity that calls my save function at the proper moments, onPause and onSavedInstance.
The onPause happens if the activity leaves the foreground and the onSavedInstance before being killed.
My activity saves the state perfectly if the user presses the "home" key but if he presses
the "back" key the onPause still happens and consequently some fields are saved in
the activity class attributes.
The problem is that when the User goes back to the activity the onCreate is called meaning (for what I understood) that the class is instantiated and therefore its attributes are null again.
If the onSavedInstance was called when the back key was previously pressed I could use it to save the activity state but it's not.
So my question is, when the user presses the back key how can I save the activity sate withou using sqlite, file saving and other persistance methods?
When the user presses the BACK button, your foreground activity is destroyed. That activity will be called with onPause(), onStop(), and onDestroy(). That activity object will then be garbage collected (unless you introduced a memory leak).
onSaveInstanceState() will be called periodically if there is a chance that the activity will be destroyed shortly but in a way where the user might be able to navigate back to it. The prominent case for this is during a configuration change, such as rotating the screen.
What you should be doing in onPause(), if anything, is persisting data using "sqlite, file saving and other persistance methods". Once onPause() is called, there are no guarantees that this activity will stick around, or that your entire process will stick around. Anything you value, therefore, should get written out to persistent storage.
The "state" for onSaveInstanceState() would be things that affect the activity's UI but are not part of the persistent data model. Much of this is handled for you automatically by Android's built-in implementation of that method (e.g., the text in an EditText), but you can add your own information to the Bundle if you wish. However, your instance state is not your data model, so anything you want to stick around needs to be written out to persistent storage.
If your concern is performance, you are welcome to cache data in static data members/singletons, assuming you do not introduce a memory leak. But, again, once onPause() is called, your process may be terminated at any point in time. Your static data members can only be a cache; your data model must be something persistent.
I am refering to http://developer.android.com/reference/android/app/Activity.html.
I have an activity which can be "interrupted" by the user, e.g. the user opens the menu to call the preferences screen. When calling the preference screen onSaveInstanceState(Bundle) is called and I can save my data. That's fine so far. But upon pressing the back button onRestoreInstanceState(Bundle savedInstanceState) is NOT called.
So how can I save my state? Do I have to do it when calling the new activity? But how?
The only way I can think of, is saving the state by passing the state to the new activity, do nothing there with the saved data, return it to the first activity and restore the state in onActivitResult. But that would mean that I have to pass data back and forth just to restore the state. Doesn't seem to be a nice solution.
Probably a bad answer, but are you sure onRestoreInstanceState needs to be called?
The point of the bundle and onSaveInstanceState / onCreate with bundle / onRestoreInstanceState is to save transient data for an activity that is in the history stack, in case the activity must be killed to reclaim some memory. If it is killed, the activity can be restored, as if it were never killed, via onCreate / onRestonreInstanceState. However, if the activity was not killed, there may be no need to restore its transient data - presumably it is just as it was.
The Android documentation is careful to point out that onSaveInstanceStae / onRestoreInstanceState are not lifecycle methods, so aren't guaranteed to be called during lifecycle state transitions. If you need to hook into certain lifecycle transitions, take a look at the lifecycle hook methods. For example, onResume is called when the activity becomes the foreground activity and onPause is called when it is no longer the foreground activity.
Have a look at the Application Fundamentals, specifically this part:
Unlike onPause() and the other methods discussed earlier, onSaveInstanceState() and onRestoreInstanceState() are not lifecycle methods. They are not always called. For example, Android calls onSaveInstanceState() before the activity becomes vulnerable to being destroyed by the system, but does not bother calling it when the instance is actually being destroyed by a user action (such as pressing the BACK key). In that case, the user won't expect to return to the activity, so there's no reason to save its state.
Because onSaveInstanceState() is not always called, you should use it only to record the transient state of the activity, not to store persistent data. Use onPause() for that purpose instead.
Basically, any persistant data should be written out in your onPause() method and read back in onResume(). Check out the Data Storage article for ways of saving data.
Your ListView should not be cleared after returning from the Pref screen unless the Activity has been destroyed while the Pref screen was showing (in which case onCreate should have been called with the saved bundle).
The savedInstanceState is only used when the Activity has been destroyed and needs to be recreated. In this case, it looks like your ListActivity has not been destroyed.
Are you clearing your ListView manually somewhere?