📜 ⬆️ ⬇️

How Android launches MainActivity

Recently, I did research on the main () method in Java and how it serves as an entry point for any Java application. It made me think, what about Android apps? Do they have a primary method? How are they loaded? What happens behind the scenes before onCreate ()? Michael Bailey spoke in great detail about how Main Thread works, so this is a quick overview of his talk, plus additional information from the Android Open Source Project (AOSP).

In this article we will look at:

  1. What happens from clicking on the application icon to launching MainActivity
  2. Find the main method of the application and find out how the main thread (also known as UI, or Main Thread) gets its purpose.
  3. Consider the role Looper & Handler plays in the messaging that ultimately leads to the creation of your Activity.

What happens when the application starts


When you run any application, a lot happens deep inside at the kernel level, for example, loading Zygote, loading classes in JVM, and for JVM - finding the main static void main (String args []) method and invoking it. In the case of Android, the JVM finds the main method main () in the ActivityThread. It then calls main (), after which the kernel transfers control to your application. So, we found the entry point - ActivityThread, but before exploring this in detail, let's look at the process roadmap to visualize the entire operation.

1 Scheme launch application


There are approximately 15 steps between the call to main () and onCreate () in our MainActivity, and in this article we will go through them. Figure 1 shows the general scheme of launching the application, showing the different interaction classes from above and the corresponding chain of methods. The steps are numbered, and when I refer to them, I will use the following notation Process3 or Process14
')
image
Figure 1: Scheme of launching the application step by step from calling main () to onCreate () in MainActivity

2. ActivityThread class


In the ActivityThread class, there are just over 6500 lines. For brevity, I identified the most important parts for us. Let's take a look at what this class and the main method associated with it do to launch our Activity.

/** * Code retrieved from https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ActivityThread.java * Modifications are indicated in the comments */ public static void main(String[] args) { //Modification - Removed unrelated initializers. //Android initializes some tracers, event loggers, enviroment initializers, trusted certificates and updates the process' state Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } // More logging // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); } 

Figure 2: The main () method in ActivityThread, which serves as the entry point for running your application.

As you can see in the code: the main () method performs three important things:

1. Prepares the main Looper (MainLooper) (Process 2)
2. Configure Handler'a (Process 4)
3. Calling the Looper.loop () method on the main thread (MainThread) (Process 6)

2.1 Preparing the main looper (Process 2–3)


The main Looper is set by calling Looper.prepareMainLooper () (see Line 8 in the code). This marks the current random thread, which does all the work of calling the main () method as the main application thread. This is exactly how the famous main thread for an Android application is determined!

2.2 Calling Handler (Process 4-5)


Inside the ActivityThread class there is a private inner class H, yes, yes, that's right, just H, which is inherited from the Handler class (see Fig. 4 and 7). In the 12th line, an instance of the H-handler is set as the main Handler stream. What is very interesting to know about the class H, as you will see later, is that it contains more than 50 state / event definitions in which your application can be located, for example LAUNCH_ACTIVITY, PAUSE_ACTIVITY, BIND_SERVICE, etc.

2.3 Calling Looper’s loop () Method (Process 6–7)


After the main thread is assigned in the same main thread, in order for us to do something in it, the Looper.loop () method is called (see Line 20). This starts the execution of messages in the Loopers message queue. Now the main thread is running and can start processing tasks from the queue.

Please note that in line 18, if the execution of the code goes further than Looper.loop () in line 17 suddenly and the application exits the loop, a RuntimeException exception will be thrown. This suggests that the loop () method ideally never ends prematurely. We will see this in the next section.

3. Infinite loop () in Looper (Process 7,8,9)


 /** * AOSP * Looper class */ public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; //code removed for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } } } 

Figure 3: Code inside the loop () method in the Looper class

As we see in the code, in the Looper.loop () method there is a message queue (line 10) and queue.next () is called inside the loop. MessageQueue is filled with the Handler-', which we talked about in the previous section (see Process 8). Pay attention to the interesting description of the condition in the for loop - there are no arguments here, only two semicolons say that it is an infinite loop. Therefore, a Looper ideally never ends if the given message is not null.

So, now we have defined the main thread executed by the Looper, we also saw that Handler adds messages to the Looper.loops () loop and processes the messages. Let's see how they together cause our Activity.

4. Run MainActivity (Process 10 to 15)


It is important to remember that this infinite loop and message processing were performed in the main () method of the ActivityThread class, because it was there that they were called (see line 12 through 17 in the code). We skimmed the Loopers, MessageQueues, and Handlers to understand the context. So let's go back to the ActivityThread class, in particular, to the inner class H, which we talked about earlier, which acts as the main Handler of the main thread.

So, we have a Looper that sends messages to our Handler, let's find out how these messages are processed. This is done inside the H class. This class contains the handleMessage (Message msg) method. Remember that all classes that inherit from Handler must override this method.

 /** * Retrieved from AOSP * H class embedded in the ActivityThread class */ private class H extends Handler { //Several Application State Identifiers ... public void handleMessage(Message msg) { //other code switch (msg.what) { case LAUNCH_ACTIVITY: { //create Activity records handleLaunchActivity(r, null, "LAUNCH_ACTIVITY"); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); ... //handle other cases eg ResumeActivity, PauseActivity, BindService, UnbindService etc. } } } } 

Figure 4: Private inner class H and its handleMessage () method

As can be seen in the code, in the 8th line there is a switch statement, in which the processing of the incoming message is determined by its contents.

One of the cases (cases) includes the launch of activity (line 11), what is interesting is that this method is designed to handle about 50 cases, which range from resuming, suspending, starting an Activity, linking Service's, processing Receiver 's, provide lowMemory or trimMemory warnings when the device memory is full, etc.

In the case of LAUNCH_ACTIVITY, the handleLaunchActivity () method is called, as shown in line 13, see Process11 on the diagram. Then this method calls another method called performLaunchActivity (), which returns an Activity object (see Figure 5, line 7).

 /** * Retrieved from AOSP. * ActivityThread class */ private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) { //... initialize graphics,do some logging, call GC if need be, etc Activity a = performLaunchActivity(r, customIntent); //... handle how to resume an existing activity } 

Figure 5: The handleLaunchActivity () method in which the Activity is created

The performLaunchActivity () method adds important information to the Activity, such as Instrumentation, Context, Component, and Intent; and also sets the Application. This method then calls Instrumentation.callActivityOnCreate () (Process 13), which is the last step before calling the onCreate () method in Activity (Process 14-15, see Figure 5 (code), lines 8-10).

 /** * @Retrieved from AOSP * Instrumentation class */ public void callActivityOnCreate(Activity activity, Bundle icicle) { //   Activity    prepareLaunchActivity(). //      onCreate() prePerformCreate(activity); //  Activity activity.performCreate(icicle); //  onCreate() postPerformCreate(activity); } 

Figure 6: Instrumentation class finally launches Activity

At the moment, your Activity is loaded with many useful variables and methods that you can use to create your new amazing Android application! All this thanks to ActivityThread, the clever work of Handler and Looper, and the huge class of 7600 lines of code that allow you to attach fragments, get context and easily manage View's - and a lot more.

That's how our Activity is created!

Original article here .

Source: https://habr.com/ru/post/345120/


All Articles