Hi guys as the title says I am trying to keep my application alive so to speak when the phone locks.
In detail my app would be open and running fine if I leave my phone alone for a while and unlock it my application is frozen and can't do anything. Now this only happens on some devices for some weird reason but I am wondering is there a way to manage these kind of things?
Thanks
Implement the onPause and onStop on your activity.....
To see if they are being called....
I have been trying to overcome this kind of issue in a few apps as well. My research points me here: Android - Activity Lifecycle as it states this:
However, if the system destroys the activity due to system constraints (rather than normal app behavior), then although the actual Activity instance is gone, the system remembers that it existed such that if the user navigates back to it, the system creates a new instance of the activity using a set of saved data that describes the state of the activity when it was destroyed. The saved data that the system uses to restore the previous state is called the instance state and is a collection of key-value pairs stored in a Bundle object.
So, we need to be leveraging the savedInstanceState Bundle for cleanly packing and unpacking each Activity and Fragment.
I'll work on an example for you ASAP.
Related
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
I am using Singleton pattern. When application goes into background the OS kills the app to release memory. When user comes back to app my application starts from same Activity User left off but my singleton instance is not available because it was released too when OS released memory.
Suppose I am at Activity 5 and I have values int a = singleton.getInt() it will give me a null pointer exception. Other local views initialized are null too. But if my application restarts from splash screen then its all good because app flow is starting from scratch. Sometimes it starts from Splash, some times it goes to Activity 5 or whatever activity User left off (which will cause null pointer exception). I have used a lifecycle Observer to monitor application state in MainApplication but what exactly is the root of this issue. Any ideas?
Why does this happen?
When the system kills your application to free the memory it saves a few pieces of data about the current state of your Application into the internal OS storage. Later on, when the user opens your app once again, the system takes that saved data from the storage and tries to restore the state of the Application.
The reason for that behavior is to make the user experience smoother. By restoring the state - the user can quickly continue doing things he was doing before quitting your application.
Some of the data that is stored in the OS storage:
Activities back stack (back stack of the Task)
FragmentManagers back stack
Bundle that you saved in the onSaveInstanceState methods
Please note: ViewModels are not saved between application re-creation. Even though they are saved upon Activity recreation(configuration change, such as screen rotation). So data that you store inside of your ViewModels (if any) would be lost!
In order to support this OS feature and ensure a smooth user experience in your application, you must design your application to not depend on any Launcher Activity to instantiate your singletons and instead use some other architectural approach that would not require your Launcher Screen to launch every time the application restarts.
What can you do?
You could try one of the following:
Save all the user input data (if any) to the Bundle in onSaveInstanceState method.
Migrate from Splash Screen Activity or Launcher Activity in favor of the Reactive approach to data instantiation.
Move your Singleton instantiation to Application class. Note: This could significantly increase the launch time of your application.
That is by no means an exhaustive list of actions you could do. I bet you would have better ideas that are applicable to your own application.
If migrating away from Launcher Activity is not possible at the moment, I suggest adding to your Activity onCreate method a check that everything is instantiated and ready to use. If not - immediately navigate the user to your Launcher Activity.
Please note: It would be good UX to save back stack and navigate back to the current activity as soon as your logic on Launcher Activity is done working.
So, as far as I have understood, once the App is not destroyed and is in the background; if the OS requires more memory the OS kills the app but saves the state (onSaveInstanceState). And when we re-open the app, it would seem like we are facing our previous activity but it actually has been destroyed and created again. If my interpretation is correct, how does the App retrieve the saved state? Does it store it in memory? For how long are we able to retrieve the saved state?
If my interpretation is correct, how does the App retrieve the saved
state?
From the Android documentation
If the system destroys the activity due to system constraints (such as
a configuration change or memory pressure), then although the actual
Activity instance is gone, the system remembers that it existed. If
the user attempts to navigate back to the activity, the system creates
a new instance of that activity using a set of saved data that
describes the state of the activity when it was destroyed.
Regarding your second question - it's the implementation detail how the OS does it and it's actually shouldn't worry us :). What's important is that it should do it reliably.
The system will keep the saved state as long as the user doesn't press Back or finish() of your Activity is not called.
The OS stores it. It calls onSaveInstanceState to let you generate a Bundle it stores, and will call onRestoreInstanceState to let you restore yourself from the state. How the OS stores it doesn't matter- maybe it saves it in RAM, maybe it serializes it to disk. What is assured is that if you go back to that activity you will be passed a Bundle object with the information you previously filled in. You do not need to retrieve the state- if it exists it will be passed to you.
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
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.