Chromium on Android: implementation and analysis of the Chromium main message loop on the Android system
Abstract: When I first came into contact with Chromium on Android, I was curious about how Chromium's main message loop was integrated into Android applications. For Android programs, once started, the main thread will have a Java-layer message loop to process user input events and other system events. For Chromium, it has its own set of message loops, what are the features of this implementation and how to seamlessly integrate it into the message loop at the Android Java layer is the topic to be discussed in this article.
Message loop and main message loop
Message loop, or event loop, is an important concept in the asynchronous programming model. It is used to process asynchronous events in a single thread. These asynchronous events include user input events and system events, timer and asynchronous tasks distributed between threads.
The Chromium system abstracts a message loop into a MessageLoop class, specifying that each thread can only run one MessageLoop instance at most. MessageLoop provides the PostTask series method to allow new asynchronous tasks to be added to the task queue. When MessageLoop finds that a new task arrives, it will round-robin its task queue and execute the task in the "first-in-first-out" mode until it receives the MessageLoop: Quit message, the message loop will exit.
Chromium divides MessageLoop into several different types based on the asynchronous events to be processed:
TYPE_DEFAULT: The default message loop can only process Timer and asynchronous tasks;
TYPE_UI: Not only Timer and asynchronous tasks can be processed, but also system UI events can be processed. The main thread uses MessageLoop of this type, that is, the main message loop;
TYPE_IO: Asynchronous IO events are supported. All IO threads that process IPC messages in Chromium create MessageLoop of this type;
TYPE_JAVA: A message loop type designed for Android. the backend implementation is a Java-layer message processor used to execute tasks added to MessageLoop. Its behavior is similar to that of TYPE_UI, however, the MessagePump factory method on the main thread cannot be used during creation.
Note: MessageLoop of this type is irrelevant to the message loop discussed in this article.
The specific implementation of MessageLoop is related to the platform. Even on the same platform, the implementation methods may be different because different event processing libraries are used. Chromium encapsulates platform-related implementations in the MessagePump abstract class. The relationship between MessageLoop and MessagePump is as follows:
The specific implementation of MessagePump provides platform-related asynchronous event processing, while MessageLoop provides the basic framework for polling and scheduling asynchronous tasks. The two are associated through the MessagePump: Delegate abstract interface.
MessagePump: the basic code skeleton of Run is as follows:
for (;;) { bool did_work =DoInternalWork(); if (should_quit_) break; did_work |= delegate_->DoWork(); if (should_quit_) break; TimeTicks next_time; did_work |=delegate_->DoDelayedWork(&next_time); if (should_quit_) break; if (did_work) continue; did_work =delegate_->DoIdleWork(); if (should_quit_) break; if (did_work) continue; WaitForWork();}
There are three special points in the above code snippet:
MessagePump is responsible for responding to asynchronous events of the system and scheduling the Delegate task for sufficient time segments. Therefore, MessagePump uses a hybrid and staggered method to call DoInternalWork, DoWork, DoDelayedWork, and DoIdleWork, to ensure that no one type of task is "Hungry" because it cannot be executed; DoInternalWork and WaitForWork are private methods of MessagePump implementation. DoInternalWork is responsible for distributing the next UI event or notifying the next IO completion event, while WaitForWork will block MessagePump: Run method until the current task needs to be executed; each MessagePump has the should_quit _ flag, once MessagePump: Quit is called, should_quit _ is set to true. Check the should_quit _ mark each time MessagePump finishes processing a type of task to determine whether to continue processing the subsequent task, when should_quit _ is found to be true, it directly jumps out of the for Loop body.
Nested message loop (Nested MessageLoop)
If we compare a message loop to a dream that requires many things, the nested message loop is a "leeching space", from a dream to another dream.
Simply put, a nested message loop is another message loop in the current message loop that executes a task, and the current message loop is forced to wait for the nested message loop to exit, to continue executing subsequent tasks. For example, when the MessageBox dialog box is displayed, it means a new message loop is entered until MessageBox's OK or Cancel button is pressed to exit the message loop.
RunLoop is a class introduced by Chromium in later code refactoring. It is mainly used to make it easier for developers to use nested message loops. Each RunLoop has a run_depth value, indicating the number of nested layers, this RunLoop is nested only when the run_depth value is greater than 1. RunLoop is a special object created on the stack. When the MessageLoop: Run function is completed, RunLoop is automatically released:
void MessageLoop::Run() { RunLoop run_loop; run_loop.Run();}
Each MessageLoop has a pointer pointing to the currently running RunLoop instance. When the RunLoop: Run method is called, MessageLoop: current () the RunLoop pointer in the message loop is also changed to the currently running RunLoop. In other words, if MessageLoop: current ()-> PostTask is called at this time, the asynchronous task will be executed in the nested message loop.
The basic usage of nested message loops is as follows:
Void RunNestedLoop (RunLoop * run_loop) {MessageLoop: ScopedNestableTaskAllower allow (MessageLoop: current (); run_loop-> Run ();}} // create a new RunLoopRunLoop nested_run_loop on the stack; // asynchronously start a nested message loop in the current message loop; MessageLoop: current ()-> PostTask (FROM_HERE, base: Bind (& RunNestedLoop, Unretained (& nested_run_loop); // once RunNestedLoop is executed, the internal RunLoop pointer of MessageLoop: current () points to nested_run_loop, postTask will execute the Quit operation MessageLoop: current ()-> PostTask (FROM_HERE, nested_run_loop.QuitClosure (); // nested RunLoop has exited when task_callback is executed asynchronously, restore to the original message queue for execution; MessageLoop: current ()-> PostTask (FROM_HERE, task_callback );
Message Loop Mechanism in Android
The basic principles of the message loop mechanism on the Android platform are very similar to those in the Chromium system. The difference is that the abstraction of the Android message loop is built on the Java layer.
In Android systems, android. OS. logoff and android. OS. Handler are two very important classes in the message loop mechanism.
Logoff is similar to Chromium's MessageLoop (or RunLoop) concept. Every thread in the Android system can be associated with a logoff to process asynchronous messages or Runable objects. The Android system thread is not associated with any logoff by default. developers can explicitly call logoff. prepare and logoff. loop To create and run logoff for the thread until logoff. Logoff interaction must be completed by Handler.
Handler allows developers to send or process messages and Runable objects to the thread's message queue. It has two purposes: 1) The handleMessage method is used to asynchronously process the message queue in their own thread; 2) send messages to message queues of other threads through the sendMessage or post method series. A thread can have multiple Handler. When logoff polls the message queue, it is responsible for distributing the message to the target Handler. The handleMessage of the target Handler is used to process the message.
The relationship between logoff and Handler is shown in (picture comes from here ):
Chromium uses the message loop mechanism of Android
Here, we mainly discuss the message loop for the main thread, that is, the UI thread, because the IO thread is created at the native layer and does not involve interaction events with the UI element, android is also a POSIX system, so you do not need to consider the integration of IO thread message loops.
As mentioned above, the message loop of the Android system is built on the Java layer, and the problem to be solved by Chromium is as follows, how to integrate the MessageLoop of the c ++ layer responsible for managing and scheduling asynchronous tasks into the control path of the Android system on the main thread. The answer is android. OS. Handler.
First, let's take a look at the specific implementation of MessagePumpForUI on the Android platform. Unlike other platforms, in the implementation of MessagePumpForUI, the Run method that starts the entire message loop processing logic does not do anything. Instead, A Start method is added to enable the message loop of the Android System in Chromium, as shown in the following code:
void MessagePumpForUI::Run(Delegate* delegate) { NOTREACHED()<< UnitTests should rely on MessagePumpForUIStub in test_stub_android.h;}void MessagePumpForUI::Start(Delegate* delegate) { run_loop_ = newRunLoop(); // Since the RunLoopwas just created above, BeforeRun should be guaranteed to // return true (itonly returns false if the RunLoop has been Quit already). if(!run_loop_->BeforeRun()) NOTREACHED(); JNIEnv* env =base::android::AttachCurrentThread(); system_message_handler_obj_.Reset( Java_SystemMessageHandler_create( env,reinterpret_cast
(delegate)));}
The Start method uses JNI to request the Java layer to create a program that inherits from android. OS. the SystemMessageHandler of Handler adds a new Handler type to the Logoff of the current UI thread. It provides its own handleMessage method:
class SystemMessageHandler extends android.os.Handler { private staticfinal int SCHEDULED_WORK = 1; private staticfinal int DELAYED_SCHEDULED_WORK = 2; privateSystemMessageHandler(long messagePumpDelegateNative) { mMessagePumpDelegateNative = messagePumpDelegateNative; } @Override public voidhandleMessage(Message msg) { if (msg.what== DELAYED_SCHEDULED_WORK) { mDelayedScheduledTimeTicks = 0; } nativeDoRunLoopOnce(mMessagePumpDelegateNative,mDelayedScheduledTimeTicks);}…}
In this case, how does the C ++ layer send requests for asynchronous tasks to the Handler at the AndroidJava layer and how does the Handler Execute asynchronous tasks at the ChromiumC ++ layer?
The C ++ layer sends asynchronous tasks to the Java layer.
Every time the C ++ layer adds a new asynchronous task to MessageLoop of the UI thread by calling MessageLoop: current ()-> PostTask *, MessageLoop :: scheduleWork initiates the action to run the scheduling task, while MessageLoop: ScheduleWork requests MessagePump to complete this action. Its call chain is:
Therefore, the ScheduleWork of MessagePumpForUI needs to send requests from the C ++ layer to the SystemMessageHandler at the Java layer:
void MessagePumpForUI::ScheduleWork() { JNIEnv* env =base::android::AttachCurrentThread(); Java_SystemMessageHandler_scheduleWork(env, system_message_handler_obj_.obj());}
Correspondingly, the implementation code of SystemMessageHandler's scheduleWork is as follows:
class SystemMessageHandler extends Handler { ... @CalledByNative private voidscheduleWork() { sendEmptyMessage(SCHEDULED_WORK); } ...}
It is not hard to see that the only thing SystemMessageHandler. scheduleWork has to do is to send a SCHEDULED_WORK message type to the Message Queue of the UI thread. Next, let's take a look at how the Java layer processes messages of this type.
Java-layer Handler processes asynchronous tasks from the C ++ Layer
When the Logoff of the UI thread receives the SCHEDULED_WORK asynchronous message, it will distribute it to SystemMessageHandler accurately, which is composed of SystemMessageHandler. handleMessage reloads the method to process the message. As shown in the code above, handleMessage executes a native method defined in MessagePumpForUI DoRunLoopOnce. Its implementation code is as follows:
static void DoRunLoopOnce(JNIEnv* env, jobject obj, jlong native_delegate, jlong delayed_scheduled_time_ticks) { base::MessagePump::Delegate* delegate = reinterpret_cast
(native_delegate); bool did_work =delegate->DoWork(); base::TimeTicksnext_delayed_work_time; did_work |=delegate->DoDelayedWork(&next_delayed_work_time); if(!next_delayed_work_time.is_null()) { if(delayed_scheduled_time_ticks == 0 || next_delayed_work_time < base::TimeTicks::FromInternalValue( delayed_scheduled_time_ticks)) { Java_SystemMessageHandler_scheduleDelayedWork(env, obj, next_delayed_work_time.ToInternalValue(), (next_delayed_work_time - base::TimeTicks::Now()).InMillisecondsRoundedUp()); } } if (did_work) return; delegate->DoIdleWork();}
The DoRunLoopOnce method gives the C ++ layer MessageLoop the opportunity to process its own asynchronous tasks, including delayed tasks and Idle tasks.
Now, with the implementation of MessagePumpForUI and SystemMessageHandler, the Chromium system can be seamlessly integrated into the Android message loop on the main thread,
Summary
The Handler class provided by the Android SDK provides great convenience for the Chromium system to seamlessly integrate its own message loop into the Android system, chromium MessageLoop sends an asynchronous message to the Handler at the Java layer through JNI. When logoff distributes this asynchronous message, the Java-layer message processor calls the native method through JNI to request the MessageLoop of Chromium to call the execution of asynchronous tasks. This completes the integration of the Chromium browser on the main thread and the message loop of the Android system.
Original article series, reproduced please indicate the original source for http://blog.csdn.net/hongbomin/article/details/41258469.