preface
We talked about the emergence of the Handler mechanism and how message queues are written.
Understand handler-1 thoroughly: Understand message queues, write message queues
Today we’re going to talk about some of the questions that haven’t been answered yet. As a dead-loop loop, how to do not block our life cycle. (~ There are benefits at the end of the article ~)
The body of the
One, the endless loop
First, we need to understand the meaning of the loop. We all know that for threads. The end of the code means the end of the thread’s life. If you want to live forever, what should you do? Obviously, as long as the code never runs out. In this case, an infinite loop clearly fits the bill:
while(true) {I am immortal ~!
}
Copy the code
So we understand one of the meanings of loop(). However, this introduces a whole new problem: since it is an infinite loop, code outside the body of the loop must not be executed.
In order to run the program properly, we need to find a way to schedule other methods in the loop. Start Other threads, polling through message queues. This is obviously a good solution, as the main thread’s loop body simply fetches messages from the message queue and executes. It doesn’t matter where the message is coming from, what thread it’s coming from, it doesn’t matter at all.
This is where the Handler mechanism comes in: it allows data to be passed between different threads (Message in this case, other threads send messages to MessageQueue, and the main thread polls and executes).
With that in mind, let’s think about an implementation for Android.
1.1. Endless Loops in Android
The loop() called in the main method of ActivityThread is clearly the body of the loop.
public static void loop(a) {
// Omit some code
for (;;) {
// Omit some code}}Copy the code
With the above analysis, we can understand:
- 1. Its existence ensures the immortality of our main thread.
- The Handler mechanism ensures that messages are scheduled between our threads.
The meaning of the loop is clear. So why doesn’t an infinite loop block our UI thread? A word to summarize:
Really getting stuck operation is the main thread, in onCreate/onStart/onResume performs a time-consuming operation in this method of life cycle, rather than the loop infinite loop. As long as the life cycle of our component is called back normally, the loop dies and dies, and it doesn’t matter to us at all. Because our code logic is all executed in the corresponding lifecycle.
The question then arises: how is the lifecycle method of the ** component called back? ** Before answering this question, let’s go over the design of the Handler mechanism:
1.2. The Handler mechanism
The main thread holds a MessageQueue, and loop() continuously retrieves Message from MessageQueue in an infinite loop. If it is not null, the corresponding method in Message is executed. Other threads get MessageQueue via LocalThread and can then send messages to the Queue.
With this in mind, we can actually answer the question: how do component lifecycle methods get called back?
The answer is that the main thread can be told the corresponding lifecycle method by sending a Message to MessageQueue in other threads when the lifecycle method should be called back.
Of course it’s easy to put it in words. There’s a lot more involved in actually implementing it in code. So let’s actually look at how lifecycle methods are scheduled in code.
Second, life cycle scheduling
2.1. Brief the process
To understand life cycle scheduling, a term must be used: Binder mechanisms. And it involves a process that we must be familiar with. Let’s take a look at this:
For the figure above. Binder client and Binder server. Simply put, the server is used to respond to client calls.
Therefore, in the case above, ApplicationThread in the App default process is the Binder client that responds to calls from the Binder client ApplicationThreadProxy in the System_server process.
Likewise, the Binder client ActivityManagerService in the System_server process responds to a call to the Binder client ActivityManagerProxy object in the default App process.
2.2、Handler + Binder
In 2.1 we talked about the Binder mechanism in paragraphs and graphs. With this foundation, we can explain how our life cycle is scheduled in conjunction with the Handler mechanism.
For an Activity, its life cycle must be called back in the main thread. This process is implemented through the Handler mechanism. Let’s take a quick look at the code in ActivityThread, which I’ll focus on later:
private class H extends Handler {
// Omit some code
public void handleMessage(Message msg) {
switch (msg.what) {
case RESUME_ACTIVITY:
handleResumeActivity((IBinder) msg.obj, true, msg.arg1 ! =0.true);
break; }}}Copy the code
The Handler polls in a loop. To keep messages flowing, extra threads must be started. Our ActivityManagerProxy is an additional thread. It is started before loop() is called:
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
// Create a Binder channel, which creates a new thread
thread.attach(false);
Looper.loop();
Copy the code
Simply put, let’s say we want to execute an Activity’s onResume() method. Here’s how it works:
Explain briefly in words: Through ActivityManagerProxy (App default process), Binder to ActivityManagerService, ActivityManagerService is then transmitted to the ApplicationThread with Binder via ApplicationThreadProxy (System_server process). We are now in the BInder thread of our App default process. The Handler sends the Message to the main thread, which then finds the corresponding Activity instance and calls back the corresponding onResume method.
C, Read F**k Code
3.1 Start of Binder threads
Let’s start with the attch method mentioned above:
private void attach(boolean system) {
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throwex.rethrowFromSystemServer(); }}Copy the code
IActivityManager is the interface class for IActivityManager, AIDL. Who is it for?
public static IActivityManager getService(a) {
return IActivityManagerSingleton.get();
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create(a) {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
returnam; }};Copy the code
Obviously we need to go back to the ServiceManager to find out:
private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
public static IBinder getService(String name) {
// Omit some code
IBinder service = sCache.get(name);
}
Copy the code
We can see here that the HashMap is used to look up the IBinder object for multiple wins. ACTIVITY_SERVICE = context.activity_service;
ActivityManagerService{
// Omit some code
ServiceManager.addService(Context.ACTIVITY_SERVICE, this.true);
}
Copy the code
Now that we know what the IActivityManager object is, there is only one ActivityManagerProxy. Because IActivityManager. Stub. AsInterface (b) according to the current process is to determine the return Stub object or the Proxy object. Our AMS is running in another process and we’re returning a Proxy object.
At this point, our extra thread is created. With this thread we can perform the corresponding lifecycle callbacks for the four major components outside the loop.
3.2, H
Since the focus of this article is on Handlers, we will skip Binder and assume that ApplicationThreadProxy calls our ApplicationThread object through Binder.
private class ApplicationThread extends IApplicationThread.Stub {
// Omit some code
public final void scheduleResumeActivity(IBinder token, int processState,
boolean isForward, Bundle resumeArgs) {
// Omit some code
sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0.0, seq); }}Copy the code
We can clearly see that the sendMessage method is used in the scheduleResumeActivity method, which is clearly a wrapped method. Let’s go inside and take a look at that:
private void sendMessage(int what, Object obj, int arg1, int arg2, int seq) {
if (DEBUG_MESSAGES) Slog.v(
TAG, "SCHEDULE " + mH.codeToString(what) + " arg1=" + arg1 + " arg2=" + arg2 +
"seq= " + seq);
Message msg = Message.obtain();
// Omit some code
mH.sendMessage(msg);
}
Copy the code
Here we can see a particular Handler implementation class H. Basically a Handler declared in the main thread
private class H extends Handler {
// Omit some code
public void handleMessage(Message msg) {
switch (msg.what) {
case RESUME_ACTIVITY:
handleResumeActivity((IBinder) msg.obj, true, msg.arg1 ! =0.true);
break; }}}Copy the code
At this point we can see that our Activity lifecycle has completed the call.
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
// Omit some code
r = performResumeActivity(token, clearHide, reason);
}
Copy the code
Four,
The questions I have are the following. Now it all makes sense.
4.1. The meaning of loop
For threads, code execution ends and the thread disappears. So for the main thread, if you want to die, the loop is preferred.
4.2 How to guarantee the scheduling of the declared cycle with the dead-loop
This is primarily through the Binder mechanism. A Binder thread is started before loop is called. When we want to call a component’s lifecycle method, Binder mechanisms handle it with ActivityManagerService (AMS), which ultimately passes it back to our ApplicationThread object. This object finally hears the Handler send the scheduled Message to the main thread’s MessageQueue, completing the normal scheduling of the lifecycle.
The end of the
This officially concludes what I want to write about Handler, although there are still a lot of holes to fill in.
-
Example: The Linux pipe/epoll mechanism.
-
For example: Binder, which is quite long, will be mentioned in the next article.
In addition, the process analyzed in this article is more like the startup process of an Activity. The following article on Binder links the Binder mechanism together with a real example of starting an Activity