Persisting state in the android Application class - android

I am developing an Android application consisting of over 10 activities. I have some state objects that I access in almost every activity and for this purpose these were implemented as global static variables in the MyApplication class.
I noticed that this approach is OK as long as the user is "in" the application. However, when he presses the home button and opens another apps and then goes back to my app through the "Recent activities" button, I see that the Android system resets the statics from the MyApplication so I have to deal with NullPointerExceptions. I know that this behaviour is caused by Android killing and recreating the application process.
I know that the best way to persist this kind of data is using SharedPreferences or SQLite, and I have no problem checking if MyState==null in the onCreate for and restoring it, but the problem is that I don't know when to properly store my state object (in prefs or database). I tried to override MyApplication's finalize() - no good, I saw that onLowMemory may not be called, I don't see how can I use onPause, OnStop and so on because I have so many activities that the serialization de-serialization would considerably slow down the app.
Any thoughts?
Thanks in advance!

It is better to not depend on the Application class unless you need to load some data, before anything else is started. Android can kill your process at any time to free resources, so your app should be able to handle this. Save all of your data in a snigleton class, and load it lazily -- check for null, and if so load on first access. It the state needs to be persistent, consider staving it file/shared prefs. If not, your app can probably live without it, so just make sure you check for null, etc.
Generally, you should persist state when activities become inactive -- onStop(), onPause(), but you can save as soon as it makes sense (e.g., the user has entered all required data). Spin off an AsyncTask to save data in the background and let the user continue their work.

Related

Use-case of Android Lifecycle functions onRestoreInstanceState, onSaveInstanceState

I recently wrote a demo app, which just needed to display some data temporarily --- I meant for the data to disappear once the app was properly destroyed by the user. Toward this, I read the page
The Activity Lifecycle , which seems to recommend overriding the Activity methods
onRestoreInstanceState() and onSaveInstanceState().
It worked great! The data was preserved through screen rotations, and sending the app to the background.
But then I would leave the app running and walk away, and when I looked at it again, the data was gone.
I spent hours trying to de-bug my app, and re-reading that page.
Finally, I read
Saving UI States. It refers to overriding these methods as "ViewModel" approach, and explicitly states that data saved this way does not survive system-initiated process death --- which explains my observation.
My main question is: what on earth is the practical application of this "ViewModel" persistence approach? What is the use-case for a persistence mechanism that randomly disposes of data when the user isn't looking?
(I guess this is an old API left over from the times when apps didn't run in the background. But I don't see that reflected in the documentation.)
A second question is, reading the first page, how on earth was I supposed to understand this unfortunate behavior? Did I miss something? (It is very long.)
what on earth is the practical application of this "ViewModel" persistence approach?
It is not a persistence approach. A ViewModel is a way of holding onto state across configuration changes. Using a SavedStateHandle with ViewModel — which maps to onSaveInstanceState() and onRestoreInstanceState() — is also useful for a fairly narrow use case:
User is in your app and does something that you don't want to save to disk or the server (e.g., the user didn't click "Save" yet)
User turns off their phone screen or switches to another app (e.g., via system HOME navigation or the overview screen)
Time passes
Android terminates your app process to free up system RAM for other apps
Within ~30 minutes of having left your app, the user returns to your app
At this point, Android wants to pretend that your app had been around all along, despite the fact that your process had been terminated. So, Android will not only start up a fresh process for you, but it will recreate the last activity the user had been on... and you get your saved instance state back as part of this.
However, this is not a persistence approach. For data you want to have survive long term, you need to save it to disk (SQLite, SharedPreferences, JSON file, etc.) or to some server. Notably, if the user leaves your app for an extended period (over ~30 minutes), Android will not attempt to restore the instance state, and your app will be started normally.
You need to use a SavedStateHandle with a ViewModel to get data persistence when the system terminates your app in the background. Otherwise it's more about sharing data between components, and surviving Activity destruction e.g. on screen rotation without having to do a lot of boilerplate handling.
Just like with onSaveInstanceState, this is purely about persisting data when the system kills your running app to recover memory, so that when the user switches to the "running" app again, it can be recreated and restored exactly as it was. It doesn't save any data when the app is intentionally stopped, e.g. calling finish(), the user backing out or swiping it away etc.
This stuff should always just work - if you were seeing your data "go missing" and the app wasn't crashing in the background, it's possible your save/restore logic wasn't working. A good way to test that is going to Developer Options on your device (if you don't know how to get that do a search, it depends on your device) and enable Don't keep Activities. That will destroy them as soon as they go to the background and it should help you test how that's handled. The fact you were handling rotations ok suggests it was a background crash though, but that depends on how you were handling configuration changes

How to handle data missing after resume

I got some issue when I developing an App.
After I minimized the app or turn the screen off, and open lot of other apps or reopen the phone after a long time.
When I restart my app, it try to resume and keep showing the same page(with fragment).
But the data I need was already been destroyed so it will be null.
The data is an object array, I know maybe I can store them in db.
But due to the data will update every time user click something.
So I don't want to save it into data base, I guess that means lot of storage I/O witch is not necessary.
I'm wondering if there is any solution to restart the hole app when things is destroyed?
Or the only way to make it happen is I handle the null array and do the reload myself?
I don't really want to do that cause I guess that will bring me many unexpected issues cause the data is related with many pages.
Too many situations I have to consider when do switching pages.
Are there any advice?
But the data I need was already been destroyed so it will be null
That is because your process was terminated and you did not save your state.
But due to the data will update every time user click something
Or, you could fork a thread to save the data as part of your onPause() or onStop() methods. There are many possibilities between "never save" and "save on every click".
So I don't want to save it into data base, I guess that means lot of storage I/O witch is not necessary
If you want the data to be there 30+ minutes after the user left the app, your choices are to save the data locally (file, database, SharedPreferences) or save the data on the Internet somewhere.
For small amounts of data over shorter time periods, you could put the data in the Bundle supplied to onSaveInstanceState() and then pull the data out of the Bundle again later (e.g., in onRestoreInstanceState() of your activity). You already should be doing this to handle screen rotations and other configuration changes.
I'm wondering if there is any solution to restart the hole app when things is destroyed?
You are welcome to add android:clearTaskOnLaunch="true" to your launcher activity, to indicate that you always want to start over from scratch whenever the user leaves your app and tries to come back to it. Users will not appreciate this, as this means that they will lose their state even for being out of your app briefly (e.g., a quick reply to a text message). This attribute does not terminate your process, but it will force the user back to the launcher activity and will eliminate any other activities that had been in your app previously.
Or the only way to make it happen is I handle the null array and do the reload myself?
That is what developers normally do, yes.

What is cleared when a user kills an app (android)?

When a user manually kills an app, what data is cleared?
I have a bit of functionality that only properly clears the data on app kill and reopen, so clearly the system is clearing some data that I am missing.
Basically, I want to know what sort of delete/clear data is called programatically so that I can try and figure out what is being kept around on close/reopen that doesn't stick around for kill/reopen.
EDIT: I am basically trying to find out the difference between finish() of an activity that contains a webview and force-close/reopen
When you force close, the app will be completely removed from the memory. For example, all services will be closed, any static variables that you might be holding, everything will be cleared. However, persistent storage such as SharedPreferences will not be affected. Every value that was committed before the force closure of the app will be remain as they were.
Also, you cannot expect the lifecycle methods to be called on Force Close. So if you're doing anything inside onDestroy of a Service or an Activity, that'll most likely fail since the method is never called.

android save session state across activities

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.

alwaysRetainTaskState doesn't work as expected. Singleton class is losing its state

On the first time my app is running, on the root activity, the user is required to select a certain options that determines which data would be downloaded for him from a database.
Once he had picked that option, the data is downloaded and kept on a singleton class that should hold that data as long as the application is running. (Even in the background)
My problem is that sometimes after the user exits my application for a while (using the home button), Android apparently kills some of the activities in my app, and somehow with them, it resets the important singleton class. Causing my app to receive all kinds of null reference exception once I try to access the data that is supposed to be kept on the singleton.
I understand that the Android OS can sometimes choose to kill an application, but allow the user to return to it from his last visited activity. Sometimes it just kills the whole application and forces the user to begin from the start.
I was looking for a solution, and I found out about "android:alwaysRetainTaskState" attribute that I can apply on my root activity. Apparently with it, whenever android decides to kill my app, it would at least force the user to begin from the first activity where I can re-download the data, instead of allowing the user to begin with a more advanced activity causing him to get null exceptions.
However when I applied it on my application it did not work, and when I returned to my application I was beginning from an advanced activity, with an empty singleton instance.
I also tried "android:clearTaskOnLaunch" and it worked, but it's an overkill, since I don't want that every time the user would return to the app, he would have to start over. I want it to happen only if android decided to kill the app.
Is there something I'm doing wrong? Is there any chance maybe there's a better solution for keeping the singleton alive?
Thanks!
Nope. There is not better solution. This is how it works. Android can kill your application's process whenever it wants to (as long as it is in the background). You can keep data in a singleton, but it is always possible that Android kills your process anyway. When the user returns to your application Android will create a new process and then create a new instance of the activity that the user is resuming. You need to put code in onCreate() of all your activities that recognizes that your singleton is gone and then does an appropriate thing (either redirects to the root activity of your application or reinitializes your singleton.
It is pretty easy to tell if your process has been killed and recreated. Just add a static boolean variable to your singleton that you set to true when it has initialized. In onCreate() of all your activities, check the state of that variable and do something intelligent if the variable is false.

Categories

Resources