I have an Activity A that opens Activity B. During B's lifecycle, it creates lots of data that is important for later use. When I leave Activity B, it gets destroyed. I want that when a user opens B next time, that important data would be restored.
So the question is, how to store that important data?
I had several assumptions:
SharedPreferences (context.getPrecerence(MODE_PRIVATE)).
This is not a good options, because it allows saving only primitive types. I need to save java.io.Serializable object (or at least Parcelable).
Static variable - not an option. I want my data to remain even if JVM destroys my process when the user navigates to some other app.
Context.openFileOutput(). Is this OK to make I/O every time I enter activity/quit it?
Something else?
You can save to SharedPreference using gson.jar. see this answer related to this
You can use a database if the data is user specific.
The database can be accessed whenever user comes back.
Or you can use Bundle (use OnSaveInstance(Bundle)).
This answer is useful in Bundles
If you simply want to retain the data through config changes, onSaveInstanceState(Bundle) is what you're looking for. Otherwise, use a database.
http://developer.android.com/training/basics/activity-lifecycle/recreating.html#SaveState
Related
Hi to anyone who can answer,
So I came across this question (it was an assignment, I already submitted it anyways). It was regarding shared preferences and explicit intents. I know both can pass data (through putString, putExtra, putInt etc and getExtra, getString, getInt). However which method is better to pass data and why? It can be in terms of functionality or just how much lesser the codes are when comparing each method to each other.
If you want to pass data when transitioning from one activity to another activity, then it's normally better to use intents to pass data.
However if you want the data you are passing to still be retrievable after the user exit your app and reopen it, then you should use SharedPreferences.
Intent is better to use when you are passing data when you are going from one activity to another. Otherwise, you should use SharedPreferences instead. And as mentioned above, if you want the data to be stored and retrievable even when the user reopens the app, then you should always go with a storage option like SharedPreferences.
those are two completely different functionalities that should not be mixed.
Intent extras are used to pass data from one activity to another. If the intention is having one activity put data and the next one receive you should use Intent extras
SharedPreferences is a very lightweight data storage. It's meant to store data on permanent memory and can be retrieved by any entity within your app. If you need data to be saved and access any moment in the future from anywhere in your app, you should use this.
I've implemented onSaveInstanceState and onRestoreInstanceState in my android activity.
These seem to work OK, but I'd like to explicitly save the instance state when certain things happen (e.g. when the user presses a button) in case the application crashes, or I terminate it via the debugger
I can call the method obviously, but that doesn't provide the correct Bundle or anything else.
I've looked around, but I can't see any way to do this. Is it possible?
If not, can anyone offer a decent solution for saving activity state at specific points in case of crash/termination?
Thanks
Bundles are only stored in memory, which means even if you manage to put the data into the Bundle it will be gone if your app crashes.
I'd suggest you to use onSaveInstanceState for what it's designed for - restoring Activity state within an active process. If you want to persist some information so it won't get lost if the app crashes you should use one of the persistent storage options.
Ideally you would have model class for the data that you are displaying. You can persist the model using SQLite and some DAO library. When you do that you can then restore from the database in onCreate of your Activity.
Depending on complexity of the information you can also choose Shared Preferences. If you have only a few key value pairs this is a good choice and you don't have to use models.
I found and read a lot of articles talking about global variables in Android, some of them suggests using an subclass of Application + declare it in the manifest file as a glbal variable container.
But some articles mentioned that This class could also be killed when system memory gets low, is this correct?
So, is it 100% reliable to use an Application subclass as a global variable container? And could somebody give me a link to some documents explaining the life cycle of an application in Android (not activity)?
EDIT:
Thanks for the answers, I think I need to explain a bit more of my question.
The situation is, I just want to share a global String variable, Activity A modifies it, and activity B reads it.
When B is currently visible and user receives a call,
If A and B are killed but Application keep untouched (is this what Google calls an empty process?), I'm OK with it.
If A, B, and Application class are all killed and when user come back my app gets a clean start, I'm OK with it.
Which I'm not OK with it is, everything was killed including the Application class, when user come back my app doesn't start fresh, I mean, it starts from Activity B, will this happen? then should I start A manually or let Application class to do the initiation? none of these ideas looks good to me...
The answer is both "YES" and "NO" - the Application class can be used as a "global variable container" in that all activities and classes can rely on it.
However, you cannot rely on the Application class (or any other class) to persist indefinitely. In other words, if the user answers their phone, your application could be destroyed and then re-created when the user completes the call and returns to your app. So, you definitely cannot rely on it to persist data, but you can rely on it to provide global access to data when you app is active.
This is the Android documentation:
http://developer.android.com/training/basics/activity-lifecycle/index.html
This is a really good post on it - read the SECOND highest voted response, in addition to the "accepted" response:
Using the Android Application class to persist data
It explains pretty clearly how your app can be killed/destroyed whether you expect it or not.
EDIT:
To clarify, if you have a variable (call it "myVar") in the Application class and a user sets it in Activity A, then proceeds to Activity B, Activity B will be able to read the change.
If Android "destroys" the application class, which can occur anytime the user is not in your app (and in rare instances even if they are...), the app will be reconstructed so that the Activity Stack is still valid but "myVar" is not set, unless you persist the data.
In other words, Android will recreate the Application class and Activity B, but there is no guarantee that it will recreate Activity A until the user does something to destroy Activity B. Also, it will certainly not "replay" the user actions in Activity A in order to recreate the app state, and in your case that means "myVar" is not reliable.
If you read the references provided, you will see that this is the case (and also I have tested it).
EDIT 2:
To persist data, consider SQLite (which is pretty complicated and there are many references) or SharedPreferences:
How to use SharedPreferences in Android to store, fetch and edit values
This class could also be killed when system memory gets low, is this correct?
Yes.
So, is it 100% reliable to use an Application subclass as a global
variable container?
If you want to use it to pass values it is not reliable.
why?
EDIT:
Which I'm not OK with it is, everything was killed including the
Application class, when user come back my app doesn't start fresh, I
mean, it starts from Activity B, will this happen?
yes. It is possible that you set a value in your application from activity A and then when you are in Activity B user leaves your app and after a while android kills your process. then user comes back wants to look at your app. android again recreates application and activity B but not Activity A and instead of fill the application variable with what has passed Activity A to it, it recreates it from default initialization. So simply you missed what has passed or set by Activity A.
Don’t Store Data in the Application Object
is there a way to prevent activity to be reloaded when navigate back to it for exemple , in case of some activitites who load data from database , it's not a good for users to wait loading data everytime they navigate to this activity.
Short answer regarding the possibility of preventing reload of Activity: no. You should always , at least in my opinion, design your app to handle the Activity lifecycle. The reload (onPause->onDestroy->onCreate->onResume) will also happen when the orientation of the device changes. Having a CPU and/or memory intensive task happen every time the Activity is created would most likely lead to poor user experience.
One solution could be to have an external class handle the database loading and let the class be accessible as a singleton. I know this is discourage by many developers but at the same time I am pretty certain to have read that it is acceptable in many Android specific cases.
Alternatively you could have a class extend Application. Then the Application class could hold on to any session relevant data. This would also give access to the data independent of which Activity is currently shown.
I am sure there is a lot of other options but this is what initially came to mind.
You might want to study the Activity Lifecycle in particular Recreating an Activity
You can save any data that you've gathered or created after displaying the activity for the first time by storing it in a Bundle onSaveInstanceState(Bundle ) method and then recovering it from the same Bundle using the onRestoreInstanceState(Bundle) method.
While invoking the activity set the intent type you want to use. Here is the documentation
For your case FLAG_ACTIVITY_REORDER_TO_FRONT may help.
But on reading your question again, I think you want to keep the data as it is and don't want to load it again. In that case, extend the Application class. Save the data in a variable in this extended class and use it gain from anywhere you want to.
When I want to save some state behavior of the activity, The docs says that we should implement OnSaveInstanceState and OnReceiveInstanceState.
They say that this will save the activity state even after destroy or restarts. I care more about destroy (the activity is completely gone) , does that mean the bundles are considered persistent ?
when I open a pdf reader, clost it and open it again i see that it opens in the same page I was in. is this implemented using Bundles or oth
To store persistent application data use Shared Preferences. Shared Preferences are simply sets of data values that are stored persistently. By persistence, we are talking about data that persists across application lifecycle events. In other words, the application (or device, for that matter) can be started and stopped without losing the data. The next time the user launches the application, that data will still be available.
Some Games use Shared preference for exemple to store the level of the game reached, the player's name ...
see this link to learn how to use Android Preference API
Preference are simular to bundles However they are persistant and bundle are not!!
Keep in mind that if you need to store persistent data you have 4 options to do this:
Using Shared Preferences
Using SQLite Databases
Using Internal Storage
Using External Storage
Bundles are not persistent, the documentation says not to count on it, onSaveInstanceState() is called when the activity is about to be killed by the system, but for a known restart (for
instance on a screen rotation.) If your activity is killed because the system needs more resources (while the
activity is in the background), onSaveInstanceState() will not be called, but onPause() will. onSaveInstanceState()
really is not meant to save persistent data, as the doc states.
You can sorta consider SavedInstanceState() permanent but it's not recommended to use it for saving application related data in a persistent manner as It's not guaranteed to be called and it's not recommended by the authors themselves.
so, Only use them for saving user interface state changes (background color, currently selected items ,..) and use other method for persistence like : SharedPreferences, Files and SQLite.
Bundles are not persistent, and the documentation for them specifies that using them for persistence is not a good idea as their internal format may change between devices or even OS versions.
SharedPreferences, on the other hand, can be persisted and are the recommended way to store information like current app state.
Some relevant parts from SavingActivityState
:
Note: There's no guarantee that onSaveInstanceState() will be called before your activity is destroyed, because there are cases in which it won't be necessary to save the state (such as when the user leaves your activity using the Back button, because the user is explicitly closing the activity).
There is no guarantee data will be stored, especially in the case where your user exits the app.
Note: Because onSaveInstanceState() is not guaranteed to be called, you should use it only to record the transient state of the activity (the state of the UI)—you should never use it to store persistent data. Instead, you should use onPause() to store persistent data (such as data that should be saved to a database) when the user leaves the activity.
So like K-ballo said, used SharedPreferences if you have persistent data to store. onSavedInstanceState() is mostly useful for storing UI related data.
As every one else has recommended use shared preference and you should do this saving in onDestroy and onSavedInstance both.
When android is going to run low on memory, its just going to kill your application and call your onSavedInstance without calling onDestroy etc. Save your context in bundle it passes in onSavedInstance. When your app comes in foreground again, android will take care of restoring your back stack of activities. But this time it will pass you the bundle in your onCreate for each activity which will have all the values you saved in your onSavedInstance while your app was getting killed.
Hope this helps.
Short answer: It's not permanent.
Long answer: From "Android Programming - The Big Nerd Ranch Guide":
When onSaveInstanceState(...) is called, the data is saved to the
Bundle object. That Bundle object is then stuffed into your activity’s
activity record by the OS
...
So when does the activity record get snuffed? When the user presses
the Back button, your activity really gets destroyed, once and for
all. At that point, your activity record is discarded. Activity
records are also typically discarded on reboot and may also be
discarded if they are not used for a long time.