Application destruction in Android: why you shouldn't use singletons
TL;DR Don't use singletons and don't store data in your Application object
Most Android developers are familiar with the basic lifecycle of an Android application - from the time it starts up until the user hits the home button to put it into the background. What is not so clear, however, is what happens after that.
Placing the app into the background does not immediately kill it. Android will attempt to keep an application process alive for as long as possible, but eventually it will close the app to reuse the memory for new or more important processes. This is where the voodoo happens.
There are three main concepts when talking about a running Android app:
- The application process
- The application object
- The activity stack
The application process
This is a normal Linux process with a unique user id in which an Android application runs. In the case of a normal app, Android will run one process per APK. It is started when needed (usually when the user opens the app) and killed by the operating system when the OS need to reclaim memory. A process can have one of five priorities:
- Foreground - processes hosting a component that the user is interacting with or a component that is currently starting up
- Visible - processes hosting a service connected to a foreground activity or a partially visible activity
- Service - processes containing already started services
- Background - processes containing activities not visible to the user that does not qualify for visible or service status
- Empty - processes that are ready to service as hosts for new applications
Processes with a background priority will get terminated when the OS needs more memory. Processes are terminated in chronological order, so that the processes which were least recently used are killed first.
The Application object
The Android Application object controls the lifecycle of the application. The application object is created whenever an application component is started and is a Singleton, managed by the Android framework (this will be important later). It can be extended by an app developer to implement additional behaviour and is often used to store data that is used throughout the application.
The Activity Stack
The Activity Stack is explained is explained in a lot of detail in the Android documentation. The important part is that the activities are pushed onto a stack along with the Intent that started them and any saved instance state. If the user hits the back button, the activity is popped off the stack and the previous activity is "revealed".
Putting the app into the background
So, what happens when the user hits the home button?
The app is moved into the background and, as described above, the process is given a priority of background. At this point it is still running, but it can be terminated at any moment. If the OS needs additional memory, it will start killing of processes, eventually getting to our process.
When Android kills the process, it will also destroy the Application object, any singletons and static variables. This is because to Android, these things belong to the process, not the app. This would all be peachy, except for one thing: the activity stack still exists!
Android stores the activity stack separately from the process and, as a rule, it is kept around much longer than the background process. This is done to improve the user experience: even if the process has been terminated, Android can recreate the app in the state that it was in before the user closed it.
Which (finally) brings me to my punchline: if Android attempts to recreate an application from an activity stack and the application depends on singletons bad things will happen. This is especially bad in cases where some data is passed through the Intent object (Android will hold on to those) and other data is retrieved from a singleton object (which has been destroyed), since you are now attempting to work with a subset of the data you normally have.
So there you have it: the reason everybody tells you not to use singletons. There are ways to work around this problem, but that will have to wait for a future article.