Android Application launch: What happens when we click an icon

Android application launch

In this article, we will see, what happens when you click an app icon. How an application gets started, what are the methods and components involved in an application launch, etc?

Every one of us is using multiple applications like Youtube, WhatsApp, Facebook, Instagram, etc in our day-to-day life, but have you ever thought what exactly happens when you click an app icon? How does it gets launched? 

Well, when we click any application a series of method calls and processes happen in the background. Behind that one single click, there are hundreds of methods that are being executed. Let’s see first how the launcher application knows, which application you have clicked.

How does launcher application know which application to launch?

The launcher application is like a container to all the applications installed on your device. It knows where exactly each application resides. It knows its location and position on your device.

The launcher application get the list of all application from the package manager and display it in the form of a grid. Whenever we install or delete any application from the device, the launcher application update the list and hence the items in the grid gets updated with new values. So whenever we click any app, the item from that grid is clicked and the details of that app are extracted like package name, etc. The package name is then translated to an intent and send it via startActivity(intent)method to launch a particular application.

Things go behind the startActivity() method in application launch

The startActivity(intent) is routed to ActivityManagerService(AMS) via IPC binder transaction. The AMS then performs multiple steps.

  1. The first step is to collect information about the target object. This is done by calling resolveIntent() on PackageManager object. Below is the code snippet of the resolveintent()
    ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) {
        synchronized (mService) {
            return mService.getPackageManagerInternalLocked().resolveIntent(intent, resolvedType,
                PackageManager.MATCH_INSTANT | PackageManager.MATCH_DEFAULT_ONLY | flags
                    | ActivityManagerService.STOCK_PM_FLAGS, userId);
        }
    }
  2. The target information is saved back to the intent object to avoid redoing the step.

  3. The third step is to check if the user has all the permissions require to launch the application. This is done by calling grantUriPermissionLocked() method. In this method, UID of the target application/process is obtained first by calling getPackageUid() on PackageManager object. Then use this UID to check if the targetPkg can be granted permission to access uri by the callingUid. URI is the location of the package.

  4. Now check if the ProcessRecord already exist for the target process. If it is null, the AMS has to create a new process to launch the target application.
grantUriPermissionLocked method
AMS: grantUriPermissionLocked() method

As you can see, a lot of steps are performed before the actual launch of an application. Now, let’s see what are the different phases of an application launch.

3 Phases of an Application launch

There are three distinct phases of an application launch.

  1. Process Creation.
  2. Binding Application.
  3. Launching activity.

Process Creation

Each android application runs on a separate process. ActivityManagerService creates a new process by calling startProcessLocked() In this method an object of ProcessRecord is created. A ProcessRecord contains the full information of the running process. ActivityManagerService will hold the ProcessRecord object for each process. So before creating the process we have to create the process record.

There are few check needed before creating a ProcessRecord object.

  1. Check if the process that we are trying to create, is it already running in the background or not, so that we need not create the process again.
  2. Check if the process is a bad process. If a process is crashed at least once, then its a bad process.
  3. Check whether we are creating an isolated process or not. Isolated process are the special process that is isolated from the rest of the system and has no permissions of its own.
  4. Check if we already have the app running or waiting for it to come up. That means we have the process ID but not its thread.
Note: If you want to run your service in an isolated process, add android:isolatedProcess="true" inside service tag of AndroidManifest.xml file.  .

If all these conditions results in false, then we are confirmed that no process has been created for that application and need to create a new ProcessRecord by calling newProcessRecordLocked(). Once the ProcessRecord is created, AMS(ActivityManagerService) will get the GIDs from the PackageManager and mount the package using getExternalStorageMountMode(). This mounting is necessary because it will integrate the package with Android OS so that it will be ready to use.

try {
    checkTime(startTime, "startProcess: getting gids from package manager");
    final IPackageManager pm = AppGlobals.getPackageManager();
    permGids = pm.getPackageGids(app.info.packageName,
        MATCH_DEBUG_TRIAGED_MISSING, app.userId);
    StorageManagerInternal storageManagerInternal = LocalServices.getService(
        StorageManagerInternal.class);
    mountExternal = storageManagerInternal.getExternalStorageMountMode(uid,
        app.info.packageName);
} catch (RemoteException e) {
    throw e.rethrowAsRuntimeException();
}

Once the package is mounted, the entry point for the process is created. Here android.app.ActivityThread will be the entry point of the process.

After all the information are gathered like entryPoint, UID, GID, mountExternal, CPU info, etc AMS ask Zygote to create a new process for the application. AMS send these arguments to ZygoteProcess which then creates a child process and returns its process ID.

Here is the call sequence of the code flow:

AMS::startProcessLocked() ->Process.start(With args like, entryPoint,processName, uid, gids, debugFlags, mountExternal, etc)->Process::zygoteProcess.start(with these args)->ZygoteProcess::startViaZygote()->zygoteSendArgsAndGetResult()

Internally ZygoteProcess sends these arguments over a socket connection to Zygote. The Zygote forks itself and instantiates the ActivitThread object and returns the PID of the newly created process.

When a process is created, every process will get a main thread that will have a Looper and handler instance associated with it. The main thread calls Looper.loop() in its every iteration of run() method to handle messages from the message queue.

Binding Application

Once the process creation is done, the next step is to bind that process with the application. This is done by calling bindApplication() method on ActivityThread object. This method sends BIND_APPLICATION message to the message queue.

The message is then received in the handleMessage() method of Handler and calls handleBindApplication(). This method does few setups like setting the timezone, setting up locale list, contact resource manager to initialize few resources, etc. Once these things are done, handleBindApplication() will invoke makeApplication() which will load application specific classes into memory.

Launching an Activity

After binding the application to the process, now its time to launch the activity. The actual launch of application activity is started from realStartActivityLocked() on ActivityStackSupervisor object. This method then calls sheduleLaunchActivity() on the ActivityThread object.

The sheduleLaunchActivity() method creates an object of ActivityClientRecord. This record contains the information of different activities present inside the application like intents, savedStates, bundles, etc.

Just like in the case of Application binding mechanism, sheduleLaunchActivity() also sends a message, LAUNCH_ACTIVITY, to the message queue.

The message is then received in the handleMessage() of the handler and calls handleLaunchActivity(). This method calls performLaunchActivity() which then further creates a new Activity on instrumentation object and also calls callActivityOnCreate() on the same object. From callActivityOnCreate() method, the activity’s lifecycle method onCreate() will get called.

Here is the code snippet of ActivityThread.performLaunchActivity() method:

Creating new Activity on instrumentation object:
activity creation for application launch
Calling callActivityOnCreate() method on instrumentation object:
calling onCreate of activity at application launch

Here is the control flow of application launch.

AMS::attachApplicationLocked()->ActivityStackSupervisor::attachApplicationLocked(app)
->realStartActivityLocked()->ActivityThread::scheduleLaunchActivity
->sendsMessage(LAUNCH_ACTIVITY)->handleLaunchActivity()->performLaunchActivity()
->Instrumentation::newActivity() then Instrumentation::callActivityOnCreate()

 

Conclusion

So, this is the complete flow of how your application gets launched when you click on its icon. This is the flow from Zygote to Activity#onCreate(). As you can now understand, there are lot of things happened behind the background. So before you start launching multiple number of apps and commenting “Ahhh!!! this phone is too slow”, just think of how many phases and steps your app has to go through before it gets started. LOL!. If you like this post, please do share it on social media like Facebook or WhatsApp. If you have any suggestions or questions please do let me know in the comment section. Or if you want to know anything about any app or software for your own purpose, write us to support@gizmomind.com, and please like our Facebook page.

Reference:

Swetabh

Swetabh is an Android Developer and a Tech enthusiast. His enthusiasm drives him to know about various technologies. Now he wanted to share his knowledge to other enthusiast who are passionate to know-how and become a GIZMOMIND.

Leave a Reply