onSaveInstanceState and finish() - android

Short question: Is it possible to save data using onSaveInstanceState() method, then call finish() on Activity and upon next start of the Activity to get the data back in savedInstanceState? Or does finish() of an Activity mean the data are gone?
If first answer is correct, I have some problem in my implementation because I am getting null in onCreate() although the data was saved. If second answer is correct, I will have to re-think how I connect my Activities together :o)

Is it possible to save data using onSaveInstanceState() method, then call finish() on Activity and upon next start of the Activity to get the data back in savedInstanceState?
No.
Or does finish() of an Activity mean the data are gone?
Yes. The saved instance state Bundle is for cases where, from the user's perspective, your activity is still around, but it is being destroyed for technical reasons:
Configuration changes (e.g., screen rotation)
Process termination (with the user returning to your app fairly quickly)
If finish() is called for other reasons — you calling it directly, user presses BACK, etc. — then the saved instance state is no longer needed and can be discarded.
As a result, the saved instance state Bundle is for transient data that you would like to retain but are comfortable with losing in the face of configuration changes and process termination, such as the contents of a partially-filled-in form.

Related

Saving the transient state DIY + Has the activity been called or recreated?

In my application, an activity A calls the activity B (via an explicit intent). Lifecycle, B may get killed and recreated etc.
When B is called (from A) it initializes some things. But when recreated it needs to just pick up from where it left. It will finish() some time, then I'm back to A, which might later call B again...
B needs to save a very small amount of data, usually just a few int values. (The rest that I need to restore it is in getIntent() which seems to be still there after the activity has been killed and recreated.) I've heard that onSaveInstanceState and onRestoreInstanceState are expensive and that they are not guaranteed. Plus, I don't need to save the state of views. (For that reason, should I override these two methods with blank ones, preventing the parent ones to get called?)
What's the efficient way to store just a few int values? Should I store the values as static fields of the activity B itself, or of the application itself, or of another class written just for that?
Also, how does the activity B know whether it's been recreated rather than called?
I've heard that onSaveInstanceState and onRestoreInstanceState are expensive and that they are not guaranteed.
They are not particularly expensive. And, for your scenario, they will be used most of the time. The exception would be if your task is not being restarted, such as:
the user got rid of your task by swiping it off the recent-tasks list
at least pre-Android 5.0, your app has not be run in ages and falls off the end of the recent-tasks list
the user force-stops you from Settings
But in those cases you will not be restarting at B, since I assume B is not your launcher activity.
For that reason, should I override these two methods with blank ones, preventing the parent ones to get called?
Not unless you are experiencing actual problems, not just rumors of hints of potential problems.
What's the efficient way to store just a few int values?
For your case, probably use the saved instance state Bundle.
If you cannot afford to lose those values even if your task goes away (and you will not be restarting at anyway), store them in a file, database, or SharedPreferences.
Should I store the values as static fields of the activity B itself, or of the application itself, or of another class written just for that?
No, because none of those will work. You specifically state that your concern is "Lifecycle, B may get killed and recreated etc.". Part of that lifecycle is that your process may be terminated, and in that case, static data members go "poof".
Also, how does the activity B know whether it's been recreated rather than called?
See if the saved instance state Bundle passed into onCreate() is not null. Or, see if a retained fragment exists, if you're using one of those.

clarification about Android documentation

Note: Even if the system destroys your activity while it's stopped, it still retains the state
of the View objects (such as text in an EditText) in a Bundle (a blob
of key-value pairs) and restores them if the user navigates back to
the same instance of the activity (the next lesson talks more about
using a Bundle to save other state data in case your activity is
destroyed and recreated).
the same instance of the activity
how it could be exact instance when it is destroyed and recreated , isn't it going to be a new memory block (another instance ), could anyone help me to clear this point ?
Unfortunately the documentation isn't very clear in a lot of areas. This is one of them.
In theory, if Android were to destroy your activity while it is stopped, then it would call onDestroy() and the GC would reclaim the memory. In actual fact, Android never destroys individual activities to free up memory. What it actually does is it kills the entire OS process instead. In this case, onDestroy() never gets called on any of the activities in the process. The GC doesn't bother to clean up anything, because the VM (Virtual Machine) just dies immediately along with everything else in the process and the entire amount of memory in use by the process is reclaimed by the operating system.
When your user navigates back to your application, Android creates a completely new process for your application and then it will create a new instance of your activity. In this case it will, of course, get a completely new block of memory for the instance. You will also see the constructor get called and onCreate() will also be called. However, since Android saves the state of the activity's views, that state will be restored by the activity by the call to super.onCreate().
There are a few cases where Android will destroy an instance of an activity and create a new one automatically. This is done, for example, during a configuration (ie: orientation) change. In that case, Android calls onDestroy() on the old instance and creates a new instance of the activity. The new instance gets the saved state of the old instance and can therefore restore the state of the views as able. In this case, since a new instance is being created, it will, of course, have a different address in memory.
Once a component is destroyed, it is effectively dead and the memory it is using can be reclaimed by the GC. Android will never reanimate a dead instance.
Hopefully this clarifies the situation for you.
EDIT Add more details
Android keeps track of tasks and the activities in those tasks in its own internal data structures. For each activity, it keeps a copy of the Intent that started the activity, the most recent Intent sent to that activity and it keeps a Bundle containing the saved state of that activity. Android calls onSaveInstanceState() on an activity to give the activity a chance to save anything that it will need to restore the activity if Android decides to kill it. The default implementation of onSaveInstanceState() saves the state of all the activity's views. Your implementation needs to save anything else that the activity will need to restore itself if it is killed and recreated by Android (for whatever reason). Your activity's private member variables and static variables are not automatically saved and restored in the Bundle, so if you need these to be able to recreate your activity properly you must save them yourself using the Bundle provided by onSaveInstanceState(). Static variables will stay around for the lifetime of the application's process, but since Android can kill the process (to reclaim resources) at any time, without warning, you also cannot rely on static variables always having the expected values (in the case where Android has killed and recreated your process).
If your activity (or process) is killed by Android and the user later navigates back to your activity, Android uses the information it has in its internal data structures to create a new instance of the activity. It then calls onCreate() on the new instance passing the saved instance state Bundle as a parameter. This can then be used to restore the activity to the state it had before the original instance was killed. Android will also call onRestoreInstanceState() after calling onStart() so that you can also do your restoration of state in that method if you don't want to do it in onCreate().
Note: Remember that anything you want to save/restore in a Bundle must be either a primitive (int, boolean, etc.) or it must implement Serializable or Parcelable.
The documentation of onSaveInstanceState() contains some useful information about this as well.
The state is stored, but the activity is destroyed to save memory. So, when the user is in an Activity A and goes to the Activity B, the Activity A is destroyed, but the state is stored. When the user presses the back button, the state will be loaded and everything will be restored.
The system does it when the activity call onStop and onStart. I didn't make any test, I'm just saying what I understood.

Android back key not letting me save activity state

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.

does object die navigating between activities?

I have an application that navigates to the same activity but each time the activity loads with different parameters. In my application it's a parsed data content retrieved from url. First thing I want to ask: When I push the backbutton of my device I get my earlier activity without being recreated. Is the objects in this activities alive and can I reference them?
Second question is if my first question doesn't make sense, what do you advice me to do?
If you look at the Activity life cycle, you'll notice that as long as your phone has enough memory, your first activity is kept in memory, and with it any member with the data it contains.
But if your phone needs to have some memory, it can kill any activity kept in background (any activity but the one being shown to the user), which means that you'll loose any data that was in your first activity.
To know which happened, keep in mind that the onResume() method will always be called when your activity is brought to foreground (either on creation, or when you navigate back to it), but onCreate() will be called only when your application is created from scratch (meaning you don't have any saved data).
You should use the bundle mechanism to save data when your activity is paused, and load it when you come back to it. Read the paragraph about Saving Activity state in Android doc to see how to use this.
You are not guaranteed that in memory data will be around once you leave an Activity. Read through this part of the dev guide thoroughly to understand the lifecycle of an Activity: http://developer.android.com/guide/topics/fundamentals/activities.html
If you need to persist information, you should override the onPause, onStop, and/or onDestroy methods of your Activity. You can then save your state using SharedPreferences, the SQLite database, or even a flat file.
In the manifest file add the following to the activity:
android:launchMode="singleTop"
In your example what is happening is when you navigate back to the activity using the back button you are bringing up the activity from the stack. When you navigate to the activity inside of the app what is happening is a NEW activity is being created, while the original is still on the stack. The singleTop launch mode will pop the activity out of the stack if it is there when you navigate to it in the app, and only create a new activity if it is not on the stack.
Without singleTop each time you launch the activity in the app it will create a new instance and you will find there are times you have to hit the back button on the same activity more than once due to multiple instances.

Android: Saving a state during Android lifecycle

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?

Categories

Resources