I was part of Android Performance team at Qualcomm and I was working on optimizing the application launch time on Android platform. The first step was to understand the app launch process in detail. Here are some results -
Android Applications are different than standard mobile applications in two major ways.
- Every Android application lives in its own world, meaning it runs in a separate process, has its own Dalvik VM instance and is assigned a unique user ID.
- Android apps are composed of different components and they can invoke the components owned by other apps. Typically, they don't have a single entry point like main() method.
Application components include :
- Activities : Encapsulation of a particular operation, optionally associated with GUI, provide an execution context.
- Services : Background tasks that run in the context of application process.
- Broadcast Receivers : Broadcast Intent listeners
- Content providers : Data storage and sharing interface of an app.
Android process is same as Linux process. By default, every installed .apk runs in its own Linux process. Also by default, there exists 1 thread per process. The main thread has a Looper instance to handle the messages from the message queue and it calls Looper.loop() in its every iteration of run() method. It's the job of a looper to pop off the messages from message queue and invoke the corresponding methods to handle it.
When does a process get started? The short version is a process get started whenever it is required. Any time a user or some other system component request the component (could be a service, an activity or an intent receiver) belonging to your apk be executed, the system spins off a new process for your apk if it's not already running. General processes remain running until killed by the system. The point is, processes are created on demand.
For example, if you click on a hyper-link in your e-mail, the web page opens in a browser window. Your mail client and the browser are two separate apps and they run in their two separate processes. The click event causes Android platform to launch a new process so that it can instantiate the browser activity in the context of its own process. The same holds good for any other component in an application.
Let's step back for a moment and have a quick look on the start-up process. Like the most Linux based systems, at startup, the bootloader loads the kernel and starts the init process. The init then spawns the low level Linux processes called "daemons" e.g. android debug daemon, USB daemon etc. These daemons typically handle the low level hardware interfaces including radio interface.
Init process then starts a very interesting process called 'Zygote'. As the name implies it's the very beginning for the rest of the Android platform. This is the process which initializes the very first instance of Dalvik virtual machine and pre-loads all the common classed used by the application framework and the various apps. Then it starts listening on a socket interface for future requests to spawn off new vms for managing new app processes. On receiving a new request, it forks itself to create a new process which gets a pre-initialized vm instance.
After zygote, init starts the runtime process. The zygote then forks to start a well managed process called system server. It starts all core platform services e.g activity manager service and hardware services in its own context. At this point the full stack is ready to launch the first app process - Home app which displays the home screen.
So many things happen behind the scene when a user clicks on an icon and a new application gets launched. Here is the full picture :
The click event gets translated into startActivity(intent) call which gets routed to startActivity(intent) call in ActivityManagerService through Binder IPC. The ActvityManagerService takes couple of actions - - The first step is to collect information about the target of the intent object. This is done by using resolveIntent() method on PackageManager object. PackageManager.MATCH_DEFAULT_ONLY and PackageManager.GET_SHARED_LIBRARY_FILES flags are used by default.
- The target information is saved back into the intent object to avoid re-doing this step.
- Next important step is to check if user has enough privileges to invoke the target component of the intent. This is done by calling grantUriPermissionLocked() method.
- If user has enough permissions, ActivityManagerService checks if the target activity requires to be launched in a new task. The task creation depends on Intent flags such as FLAG_ACTIVITY_NEW_TASK and other flags such as FLAG_ACTIVITY_CLEAR_TOP.
- Now, it's the time to check if the ProcessRecord already exists for the process.
If the ProcessRecord is null, the ActivityManager has to create a new process to instantiate the target component.
(Continued in part2...)