I'm building a new android app which uses data from a server through different activities.
currently I have 2 activities, the main one connects to the server and creates the singleton object (which is shared for all activities) using the data from the server.
obviously I have an instance of the data held in the singleton class, and get the object from a static method.
I know that this is not the best way to implement a shared data between activities - I can pass them through intents (but I'm planning to create more 3-4 activities which uses the same data so why to pass them when you can have them in a static context global for the app).
What this implementation got me into is this problem:
when the user switches to another app at the second activity, and my app stays for some time on background, android frees the memory used by the app, and when returning to it, I get null pointer exception when using one of the fields of the singleton object!
I solved the problem by returning to the main activity (if the object is null) to recreate the data, but this makes the activity reconnect every time it's destroyed (it's not optimal since the data on the server doesn't change much).
I know that I have to save the server data onDestroy and to recreate the object every time I return to the activity but this must happen on every activity, and the data is about 4-5KB meaning it needs to be written to a file, and parsing it takes time (accessing phones SD card).
I'm starting to think on using Parcelable object to share the data through intents - I think android can save and restore the data of the intents automatically and in a optimal way.
(ref: http://bimbim.in/post/2010/09/27/Android-Passing-object-from-one-activity-to-another.aspx)
The question is, will intents solve my problem?
Is there another ways? Better, faster ways?
Thanx!
I am sure the problem relates to the life cycle of activities. When Android kills your activity, you can save your data as a bundle by using your main activity's onRestoreInstanceState() method, so when the activity gets recreated again, you can restore your data on the activity's onCreate() method (see here as an example). You could also use SharedPreferences in your activity's onResume() and onPause() methods. With both approaches, the idea is the same: that you save your object/data temporarily on disk, and restore it later.
Related
I have a question, why we using Parcelable/Serializable when passing data between activities. As I read in the documents around internet, Parcelable/Serializable is used when we need to pass data between processes. But all the activities of the same application run on the same process. Even Service started by an Activity run on the same process too. Then why we need to implement Parcelable/Serializable in this case ?
The app containing the parcel receiving activity might get destroyed and later be recreated so the data must be persisted to some kind of temp file.
Example:
parcelSenderActivity s is active
s calls parcelReceiverActivity r with parcable intent-extra
r is interrupted due to a phone call,
android removes app containing r and s from memory
after phone call is finished r is recreated by reloading it with parcable intent-extra in a differend process/thread
this recreation is only possible if the intentparameter can be persisted to a femp-file.
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.
Regarding application wide state, I have read many discussions about POJO Singletons vs. subclassing Application class.
Regarding saving state, I've learned about Activity.onSaveInstanceState.
But since Application doesn't really have Lifecycle methods, how can I save state which is used from several Activities?
My first idea was to use onCreate() and onSaveInstanceState() in my main Activity, but what happens if user pauses app while being in another Activity? When the app comes back to front, only this activity, but not my main activity will be recreated, right?
Will I have to do the same onSaveInstanceState and onCreate in ALL my activites or is there another way?
how can I save state which is used from several Activities?
Update your persistent store when the data changes. There is no "instance state" that is "used from several Activities", by definition.
My first idea was to use onCreate() and onSaveInstanceState() in my main Activity, but what happens if user pauses app while being in another Activity?
Eventually, your process gets terminated. "Instance state" (e.g., onSaveInstanceState()) may be supplied back to you again later, depending on circumstances. Anything you absolutely need to hold onto needs to be put into a persistent store (database, SharedPreferences, file), usually at the time that data is changed, much like how software has been written for the past 60 years.
What is the best way to maintain session information across multiple activities?
The question What is the best practices on Android to keep data between activities deathes/restarts for the whole application session? has some ideas, but does not really help me.
*My application has multiple activities with the manifest tag singleTop. They essentially work as different tabs - they each maintain their own set of fragments and back stack and so putting it all into one activity would break navigation for the user.
I am currently saving the session data as a static singleton created by my Application subclass. This works fine most of the time, except when the entire application is killed by the OS to save memory (as mentioned in the above link), say, when the user gets a call on a device with low RAM.
The only notification the app has that it is going to be killed (as far as I know) is
Activity.onSaveInstanceState(Bundle outState)
So the problem is this: onSaveInstanceState will eventually be called on every activity, not just the top-most one (the one that will appear when the user eventually returns to my app). When each non-top activity resumes, I could use Activity.onRestoreInstanceState(Bundle savedInstanceState) to restore my singleton session, but non-top activities would have old copies of the data (they may have been navigated-away from long before the user got the call).
One solution would be to only restore data to the singleton session Only if it is currently empty, but this relies on the first activity to receive Activity.onRestoreInstanceState being the top activity. This is not always the case - if the user gets a call and then returns to the app via the launcher icon, then the Main activity will be resumed first and brought forward, not the activity the user was on when they got the call.
A simple notification in the Application class that the application is being killed by the OS (not the user) is really what I need - I would then save the session to a file, and read it back on the first call to Activity.onRestoreInstanceState, but AFAIK this doesn't exist.
If you have some data that you want to store when app closes and reload them when app starts, you can have a mechanism to store and retrieve them on application's shutdown and startup. To do this override onStop() and onStart() methods on Activity's lifecycle.
And I think the best way to store and retrieve data in these two methods is SharedPreferences.
I would simply insist to use Application class to save the session. Using Application class you will be able to access the session everywhere were you are having Context so probably you can access in your whole Application. Application class also maintains the value after you Application is closed so its better to clear previous session when your Application is re-launched.
As the main use of Application class is to maintain global state of variables so that they can be used throughout the Application with updated values.
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.