Some background on a problem that I have been encountering: in my app I have a singleton object that I use regularly to access things like id and token for network calls. Sometimes when the app is killed in the background, this singleton loses its state. However, when the app is opened again and starts up in some Activity past the launcher Activity, the singleton is null.
I am in the process of refactoring this, but have been agonizing over how to ensure that the singleton will always be present even in app restart, but I'm not sure what Android does when the app restarts from being backgrounded.
I went through source code in some of the libraries we use in the app (Facebook, Intercom) to see how they manage their singletons and why their static variables seemed to just always be present, and came upon a theory.
So on a normal app cold start, the app behaves like this:
Application.onCreate() -> Launcher.onCreate() -> Activity A -> Activity B
Say the user was in Activity B and backgrounds the app. After using some other apps, they come back to my app but it has been killed at some point in between. The lifecycle then becomes this:
Application.onCreate() -> Activity B
I think the problem is that I initialize the singleton in the launcher Activity, and as a result, when B tries to get a value from the singleton, it comes up null. If I initialize the singleton in Application.onCreate(), it will always be initialized when the app is pulled up again. Is this correct?
TL;DR An application that is killed and brought to foreground again will always call its Application.onCreate() and then forward directly to the Activity it was on. Therefore, app initializations that are critical to the app functioning should live in the Application onCreate().
A application that is killed and brought to foreground again will always call its Application.onCreate() and then forward directly to the activity it was on. Therefore, app initializations that are critical to the app functioning should live in the Application onCreate().
Correct.
I think the problem is that I initialize the singleton in the LauncherActivity, and as a result, when B tries to get a value from the singleton, it comes up null. If I initialize the Singleton in Application.onCreate(), it will always be initialized when the app is pulled up again. Is this correct?
In your case the problem is really in that. However, if by "it comes up null" you mean the singleton instance is null then it is not how a singleton is supposed to work. No matter where you call singleton's methods from, its instance should not be null.
Yes, the Application.onCreate() is called always when the app comes to foreground, so every Singleton that you need to initialize must be there.
Related
I am trying to use a static singleton DataManager that makes network requests and holds arrays of data for my Activity, instantiated in my Activity's onCreate() class, but the idea that the Activity gets destroyed every time there is an orientation change is tripping me up. I don't want to re-create a new singleton and re-populate it with data every time the user changes the orientation or comes back to the screen.
Even if I make DataManager a Service, if I make it a Bound Service, it seems like the Service will get destroyed whenever my Activity gets destroyed, but if I don't make it a bound service and use startService() and stopService() in my Activity, it also gets destroyed whenever my Activity is destroyed.
Also, if I use onSaveInstanceState() and onRestoreInstanceState() to save my instance of the singleton, it possible that my singleton would get destroyed when my Activity is inactive, since there is no longer a pointer to it. Then Activity B using the same DataManager class could create another DataManager instance while Activity A is inactive. Then Activity A wakes up, inflating another Data Manager, giving us 2 DataManagers that are no longer singletons and may have inconsistent data.
I have read elsewhere that I should not subclass Application to maintain app state, but I don't understand how it would work any other way. Thanks for any clarification.
Subclass the application class and then instantiation your singleton within the application.onCreate() callback. This way it will be available for the lifetime of your application rather than the lifetime of a single activity. Careful that this won't be garbage collected until someone kills your app so don't have too many "Global" singletons.
DO NOT do the work in Application.onCreate(). You will be slowing down your application start up, no matter what happens. This is not advised for Android applications, you want your app to start promptly.
Instead, if you really need a singleton, have it construct lazily when it's necessary.(If you are sure you will use it, you you can also force the construction asynchronously from a separate thread when your activity starts). When your activity gets destroyed, it doesn't mean the whole process will be immediately torn down, so your singleton will stay alive.
Also, if you are using a singleton, make sure to be able to clear it when memory is low. You will need to implement Application.onTrimMemory(int) and clear the singleton from there.
Scenario:
I've four activities in my Android Application, lets say A, B, C and D. There is one Constants.java class in the app which extends Application class in order to maintain global application state. The Constants class have all the constants variables of the app. The activity flow is like this A-->B-->C-->D. When back button is being pressed from Activity A, I'm calling finish() method which will finishes the activity A and closes the application. After that if I'm opening the app from all apps, there is a variable in Constants.java whose value persists from the last launch. The same thing is not happening when I'm doing System.exit(10) followed by Process.killProcess(Process.myPid()) from activity A(on back pressed).
Questions:
Will finishing all activities by calling finish() of each activity will close the Application(Its process)?
How the value of a variable persists even if its all activities are finished(closed)?
Is it fair to call System.exit(10) followed by Process.killProcess(Process.myPid()) for exiting the application?
Update:
How can I clear the application constants on exit of the application(Back press of the HomeActivity)?
1) No, Android does not guarantee so. It's up to the OS to decide whether to terminate the process or not.
2) Because the Activity instance still lives in the Dalvik VM. In Android each process has a separate Dalvik VM.
Each process has its own virtual machine (VM), so an application's
code runs in isolation from other applications.
When you call finish() this doesn't mean the Activity instance is garbage collected. You're telling Android you want to close the Activity (do not show it anymore). It will still be present until Android decides to kill the process (and thus terminate the DVM) or the instance is garbage-collected.
Android starts the process when any of the application's components
need to be executed, then shuts down the process when it's no longer
needed or when the system must recover memory for other applications.
3) I wouldn't do so unless you have some very strong reason. As a rule of thumb, you should let Android handle when to kill your application, unless there's something in your application state that requires an application reset when it loses focus.
Quotes source
Will finishing all activities by calling finish() of each activity will close the Application(Its process)?
No, it should not. This is because Activities are not the only component of an app. Services, BroadcastReceivers can still run without a UI. By keeping the Application process running while it can, Android ensures faster response to the user opening the app again, or faster launching of Services etc. as the Application instance doesn't need to be recreated.
Your process will only be terminated if you or another app explicitly kill it, or the system kills it to free resources.
How the value of a variable persists even if its all activities are finished(closed)?
Your variable is in the Application class, which follows a Singleton model, and hence your value will persist until the entire app process is killed.
Additionally, finish() only calls onDestroy(), which is not a deconstructor, as explained here. So even Activity variables may persist after finish has been called, as your instance is still kept around until Android feels the need to destroy it, or your process is killed.
Is it fair to call System.exit(10) followed by Process.killProcess(Process.myPid()) for exiting the application?
While this will kill the app, it is not recommended. See this question for a variety of reasons for that.
for your Update:
How can I clear the application constants on exit of the application(Back press of the HomeActivity)?
Override onBackPressed inside your HomeActivity and before call super.onBackPressed() clear all the variable. If you have a setter for a String located inside your Application subclass:
#Override
public void onBackPressed() {
getApplication().setMyString(null);
super.onBackPressed();
}
if you have plenty states to clear you can write a method wich reset all the fields and call it:
#Override
public void onBackPressed() {
getApplication().clearAllMyFields();
super.onBackPressed();
}
Of course those field have not to be marked as final or static final
Android will keep the process until it needs to reclaim the process memory. That way, if the user immediately returns to your application, your application will appear quicker.
The value will persist until the application is killed.
Should not using System.exit(10) followed by Process.killProcess(Process.myPid()) for exiting the application?
You should really think about not exiting the application. This is not how Android apps usually work.
First of all, I'm new in android developing...
I had read some article in android API guide and feel confused about the component life cycle with the hosting process.
Here is my understanding:
Android system may kill some activities in a process or the whole process in low memory situations,which means there is a possibility that a started activity may die, but process still alive.
If a service is started and not call any stop method, when in extreme low memory, this service is killed by system with its hosting process, not just the service itself, means this circumstance should not occur:service is killed by system, but hosting process still alive.
When an app starts, the user navigates activity1 -> activity2 -> activity3 and none of them call finish(). Next, the user navigates to another app's activity and plays with it so long that the former app process is killed by the system. Now the user navigate back to activity3 in the back-tracking stack, what will happens? The former app process restarts with only activity3 recreate?
Anything wrong ?
There is no need to switch to another app's activity: from the moment that you leave the first activity to go to the second activity; there is a clear possibility that the first activity might have been destroyed when you return on it from the second activity (back-tracking) and when you go to a third one, either the first one, the second or both of them can be possibly destroyed in the mean time. In fact, you don't even to leave an activity to see it destroyed; as this will automatically happen simply if you switch from the portrait mode to the landscape mode and vice-versa by rotating the device.
When you return to an activity, the onRestart() function will be called if the activity has not been destroyed in the mean time. If it has, the "onCreate(Bundle savedInstanceState)" will be called instead but with the argument "savedInstanceState" set to a non-null value (ie, it will point toward a valid Bundle object) if you have took the precaution of saving the current state of the activity in the "onSaveInstanteState(Bundle outState)" function when the activity has entered the process of being destroyed by the system. The system will always call this function before destroying an activity when this one need to be restored later for back-tracking. Of course, it won't be called after a call to finish() because this will also remove the activity from the back-tracking stack.
Finally, in Android, the coupling between activities in the same application is very loose. When it comes to the use of resources, there is not much of a difference between switching to an activity from the same application or from another application. In many ways, activities will behave like if there were all fully independant applications when it comes to running. This is why you always need to use an intent to start an activity; even when it is from the same application.
Based on my understanding ...
In android during low memory situations, first activities would be removed from memory where the onDestroy method gets called.
This is not the case always. It depends on how the service is started i.e whether from onStart or through Binding the service with a component.
Once the former app process is killed, then when the user launches the application, he will be taken to activity 1. Launching the activity in same task or different task depends on launch modes used (single task, etc)
The android app I am working on overrides the Application class to store lightweight state (username, gps location, etc) in static vars. Most of this state is set in OnCreate of the launch activity (username retrieved from prefs, location listener runs). Is it safe to rely on the launch activity to initialize the Application class? Are there any cases where the Application class might be re-created without the Launch activity also being created?
The question comes up because I ran into a null pointer exception accessing a variable in the Application class on resuming the app after the phone was asleep for several hours (the app was left in the foreground before phone went to sleep). Is it possible that the process was killed while the phone was asleep and on waking the phone, the Application class was re-created, the top activity in the stack was resumed, but the launch activity.onCreate wasn't run thus the Application class wasn't initialized?
Note that I have tried to test these kinds of scenarios by Forcing the App to stop using Settings / Manage applications. However, I'm not able to recreate the problem. On the next run, the Application class is created, followed by the launch activity.onCreate.
Is it safe to assume that the Application class instance will exist as long as the process, and that when the Application class is created it is equivalent to "restarting" the application ie. start with a new activity stack (and first activity on stack is the launch activity)?
No. Your entire application can be killed and recreated with the task stack intact; this lets the system reclaim memory on devices that need it while still presenting a seamless illusion of multitasking to the end user. From the docs:
A background activity (an activity
that is not visible to the user and
has been paused) is no longer
critical, so the system may safely
kill its process to reclaim memory for
other foreground or visible processes.
If its process needs to be killed,
when the user navigates back to the
activity (making it visible on the
screen again), its onCreate(Bundle)
method will be called with the
savedInstanceState it had previously
supplied in
onSaveInstanceState(Bundle) so that it
can restart itself in the same state
as the user last left it.
That is, the process (which the Application is tied to) can be killed off but then restarted, and the individual activities should have enough info to recreate themselves from what they've saved before being killed, without relying on global state set in the process by other Activities.
Consider storing persistent shared state that needs initialization by an Activity in either a SharedPreference or SQLite database, or passing it to Activities that need it as an Intent extra.
You can test the scenario by killing the process of your running application.
Step 1. Open your app, and then press Home button to hide it into background.
Step 2. Invoke adb shell
Step 3. Enter command su (you have to get ROOT permission in order to kill process)
Step 4. ps (list all running process ID and find yours)
Step 5. kill 1234 (assume your application running on process 1234)
Step 6. Then, go back to your device and click the launch icon again. You may find the last activity on activity stack is reopen. You may also find onRestoreInstanceState() method is called for the activity.
In short: do initilization in YourApplication.onCreate, not some LaunchActivity
Docs to check:
- Processes and Threads
- API Guides > Activities
Is it safe to rely on the launch activity to initialize the Application class?
Yes, as long as you remember that Application can exist longer that Activity and the Activity may be killed and recreated. I am not sure what Intent will resurrected Activity get: LAUNCH or VIEW
(For scenario when activity was killed as too heavy, while there was long running service binded to app )
Are there any cases where the Application class might be re-created without the Launch activity also being created?
yes, if the last visible activity was not LaunchActivity
check Android application lifecycle and using of static
Is it possible that the process was killed while the phone was asleep and on waking the phone, the Application class was re-created, the top activity in the stack was resumed, but the launch activity.onCreate wasn't run thus the Application class wasn't initialized?
If there were several different Activities launched A, B, C and them whole process killed, then I think Android OS is good with only creating Application and C activity, while A and B would be re-creted on access, that is on returning to them.
Is it safe to assume that the Application class instance will exist as long as the process,
yes
and that when the Application class is created it is equivalent to "restarting" the application ie. start with a new activity stack (and first activity on stack is the launch activity)?
I not sure when restarting the launch activity would be called first,
but the last, i.e. the one that user should see.
"I ran into a null pointer exception accessing a variable in the Application class on resuming the app"
Check this link..
http://www.developerphil.com/dont-store-data-in-the-application-object/
This doesn't appear to be well documented or I missed it, so before I run a bunch of my own tests I was wondering if anyone already knows the answers to some of these questions.
First off, when I say "Application" I am referring to extending the Application class. http://developer.android.com/reference/android/app/Application.html
The questions I have are as follows, some are related.
When an a user leaves an Activity from within the Application, and goes to the Activity of another application, does the Application somehow get paused as well, even though it doesn't have an onPause()? Or does it continue to live unpaused until all of it's activities are destroyed?
when does the Application stop? When all of it's Activities are destroyed?
Is there ever a chance that one of the Applications Activities could be running without an instance of the Application, or will the Application class always exist if one of the Activities does?
If there is some process running on the Application, and it's Activities are all paused, will that process continue to run?
Is the Application effected by rotation in any way or does rotation only change Activities?
Thanks
As you say the application does not have onPause so nothing happens to the application. When onPause gets called in your Activity nothing special happens, your Activity continues to run and can do whatever it wants including run new threads, timers can go off, whatever.
I believe what you are asking is: when is an Application destroyed and when the onTerminate method in an Application called? The answer is hard to pinpoint and is up to the system, it does not necessarily happen when all activities get onDestroyed called. In fact even when onDestroy is called, your Activities aren't necessarily garbage collected. When the system gets low on memory the process that your Application lives in can be killed, meaning your Application will disappear; onTerminate may or may not be called. At that time all the Activities, Services, etc, are killed too.
The Application is always instantiated first, an Activity must have an associated Application, just like how you define it in the AndroidManifest.xml.
Processes never pause in Android, the onPause method does not actually really do anything other than tell you to pause things in your app. Other than that the process keeps chugging away, your threads keep running, even the main thread receive Intents with a BroadcastReceiver.
The Application gets rotation callbacks in the Application's onConfigurationChanged(). I'm not sure if you can disable that since there is no configChanges attributes supported by application tags in the AndroidManifest.xml.
A good comparison to Application is static field in any of your classes. The static fields will live as long the process is not destroyed, just like the Application. Static fields can be accessed by all Activities, Services, etc (assume the static fields are public), just like your Application.
Good Luck!
Jacob
The easiest way to understand this is to just forget that Application exists. Application has nothing to do with the application lifecycle. It is just a global on a process, that can be useful for some things, but is not needed for anything. Everything about how an application runs revolves around the Activity, BroadcastReceiver, Service, and ContentProvider components declared in its .apk.
An instance of Application can continue to exist after your last Activity is destroyed. Even if ALL activities are gone (ie. have all had their onDestroy methods called), the Application instance could still exist.
This application instance could be "re-used" for what you might otherwise think are two separate runs of your application.
This is all explained in detail here: http://developer.android.com/reference/android/app/Activity.html. If you read through it, you should understand everything.
Real quick:
Every activity has an onPause. You can choose not to override it, but it'll get called nonetheless. As soon as you switch away, onPause will be called.
Define "stop". Define "Application". The process may linger around forever, but it'll simply sleep and wait until one of its activities is started.
It's impossible for an activity to exist without being instantiated.
Every code executed runs in a process, so there's always one process for your app. The process will continue to exist after you switch to a different app, but it'll be in sleeping state. Android could at any time kill the process if system resources run low.
Every time you rotate the screen, your activity will be destroyed and recreated, unless you specifically disable that.