Android is an operating system with multitasking, allowing several applications to run at the same time. Developers coming from a different platform may find this a notable change when applying this concept. When starting to develop an Android application, it’s necessary to understand how it works in order to integrate it as well as possible; here we’ll talk about the design, the features, the use, and its development, as explained by Google engineer Dianne Hackborn:
Mobile devices have technical limitations that desktop or web systems don’t have. This article explains the four key principles behind the design of multitasking in Android.
- The choice was made to use memory management based on “temporal locality” (yes, something like a memory cache); in other words, apps aren’t actually closed when the user stops using them, since the tendency is to use a small number of applications throughout the day.
- Mobile devices don’t have the luxury of swap memory space, so they have very restrictive memory limits.
- Switching from one app to another needs to be instantaneous, in under 1 second to launch a new application. Users tend to use only a few apps — for example, they’re watching a video, get a call, and afterward want to keep watching the video. Delays of more than a second can cause users to reject the experience.
- The available API must be sufficient to fulfill Android’s famous philosophy that “all applications are created equal”, both “core” apps and “third-party” ones. This means background music playback, data syncing, GPS navigation, or use of the vibrator are all created without distinction.
The first two requirements are somewhat contradictory. The goal was for the user not to have to worry about closing the app, so it would seem like apps were always running. At the same time, mobile devices have strict limits on memory use, so a system degrades, or even starts failing very quickly, as soon as it needs more RAM. A desktop computer, by contrast, simply slows down its exchange rate with swap space. These considerations were key to Android’s implementation.
The Android Lifecycle
The fundamental mistake when talking about multitasking in Android is not distinguishing between a process and an application. In Android these aren’t tightly bound entities: several applications can share processes, or an application can make use of multiple processes depending on its needs.
The fact that you can see an application’s process “running” doesn’t mean the application is actually doing anything. It may simply be that the operating system has decided this for various reasons, and has decided to keep it around in case it needs it again soon. Likewise, it may leave an application inactive only to return to it a little later, right where it left off.
One of the keys to how Android manages applications is knowing what it does when a process isn’t closed cleanly. When the user exits an application, the process stays in the background, ready to run again if needed (for example, downloading web pages), and can immediately return to the foreground. As long as it doesn’t run out of memory, all processes will remain suspended.
Of course, there’s a limited amount of memory, and the operating system has to decide when to get rid of processes that aren’t needed. This is what’s called “the Android lifecycle“, the set of rules that decide which process should be removed. These rules are based both on the importance of a process, according to the user’s current experience, and on how it’s been used based on past statistics.
Once Android determines it needs to remove a process, it simply kills it. The kernel can immediately reclaim all the resources needed for the new process, without requiring the application to be well written or to respond correctly to an exit command. The operating system takes control so that memory failures don’t occur.
If a user reactivates an application that had been killed, Android has mechanisms to start it up again in the same previous state, in order to preserve the concept that “all apps are running all the time”. This is done by restoring the data visible to the user, the Activities (an activity is the screen we see in the foreground when we run an application). This last state is generated every time the user leaves the app, not when it’s been killed, so the kernel can later freely kill it without depending on the application.
In a way, the process manager can be seen as a form of swap: application processes represent a certain amount of memory in use; when little memory remains, some processes can be killed, and when those processes are needed again, they can be started from their last saved state
Running in the Background
So far, we already know how apps are implicitly used in the background. This is fine for completing downloads, for example, but what happens when we need to explicitly tell the operating system that it should run in the background? Data syncing, GPS, music playback…
The app needs some way to tell Android: ” hey, keep it low-key” . There are two essential mechanisms for background applications, represented by two types of components that can be declared explicitly in its manifest (we’ll talk about that another day): broadcast receivers and services.
BroadcastReceiver
A BroadcastReceiver allows an application to run in the background for a short period of time as a result of something that’s happening. It can be used in many ways to build higher-level facilities: for example, LocationManager can send a message when it detects interesting changes at a given location. Since information about the receiver is part of the application’s manifest (manifest.xml), Android can find and run the application very efficiently, even if it isn’t currently running.
When a “receive” event is handled, the application is given a time quota to finish. If it doesn’t complete within that time, the application is considered to be misbehaving, and its process is moved to suspension.
It’s a way for the app to tell the operating system: “I’ll be done in 10 seconds“.
In short, they’re used when an external stimulus is detected. In any case, they aren’t suitable for long, ongoing transmissions, such as network operations.
Services
A service allows an application to run background operations for longer, in practice. There are actually plenty of other functions that services provide, but here their fundamental purpose is for an application to say “hey, I’m going to be here for a while, I’ll let you know when I’m done” An app controls its service. It says when it runs and when it stops.
Services don’t provide a proper client-server model; their use is optional. When starting an application’s services, Android simply creates an instance of the component within the application’s process to provide its context.
Process management for services is different from BroadcastReceivers, since an unlimited number of services can request to run for an unknown length of time. There may not be enough RAM available to keep the services running, so there’s no guarantee they’ll complete successfully.
If RAM is low, services will be immediately killed, just as background processes are. However, in their case, Android can restart their process later, once more RAM is available.
Informally, you could say services are always acting like a “cry-baby” saying “I don’t want to die”, and in order not to kill it (when RAM is needed) Android wants to see that there’s user interaction — for example, when background music is playing, Android knows the user is aware that process exists thanks to the notification bar where the player can be seen, and therefore it won’t try to kill it.