Remember to read the article trilogy, like, comment, and retweet.
Wechat search [programmer Xiao An] attention is still in the mobile development field of the elderly programmers, “interview series” articles will be published in the public account.
1. Introduction
The handler sends the Message to MessageQueue. The handler sends the Message to MessageQueue. The handler sends the Message to MessageQueue. In an infinite loop, Looper retrieves Message processing messages from MessageQueue, and since mesage.target is the current hanlder, it ends up in handleMessage (). In fact, probably are right, before the interview, I also said so, and no interviewer in-depth asked, this time just have time to study the source code system, after all, still want to know why.
2. Usage
package com.example.test.myapplication; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.widget.Toast; public class MainActivity extends Activity { private Handler handler1 = new Handler() { @Override public void handleMessage(Message msg) { if (msg.arg1 == 1) { Toast.makeText(MainActivity.this, "hanlder1", Toast.LENGTH_SHORT).show(); } super.handleMessage(msg); }}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { Message message = handler1.obtainMessage(); message.arg1 = 1; handler1.sendMessage(message); } }).start(); }}Copy the code
This is the most common usage, so let’s use it as a starting point.
3. Source code explanation
The main method of ActivityThread will be called first. The main method will call Looper.prepareMainLooper(); MessageQueue: MessageQueue: MessageQueue: MessageQueue: MessageQueue
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
AndroidKeyStoreProvider.install();
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Copy the code
You can see looper.prepareMainLooper () in line 23; The prepareMainLooper () method creates a Looper object for the application by default.
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper ! = null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() ! = null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }Copy the code
SThreadLocal creates a Looper object by calling prepare (). SThreadLocal creates a Looper object by calling prepare (). SThreadLocal creates a Looper object by calling prepare (). This validates what many people say about one Looper object per thread. In the Looper constructor, the MessageQueue object is created. Let’s look at the Looper constructor:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
Copy the code
Yes, a Looper creates a MessageQueue at the same time as a Looper, so many people say that a Looper for a MessageQueue comes from this. Now that Looper and MessageQueue have been created, let’s see how to send a Message.
To find out where to send a message and how to handle it, there is only one way to follow the source code:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
Copy the code
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
Copy the code
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}
Copy the code
SendMessageAtTime () passes in two arguments, MSG is the message object we send handler.sendMessage(message). The uptimeMillis parameter represents the time to send the message, and its value is equal to the number of milliseconds since the system started up plus the delay, which is zero if you’re not calling sendMessageDelayed(). Method ends with a call to enqueueMessage().
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
Copy the code
Queue. EnqueueMessage (MSG, uptimeMillis). Queue is the MessageQueue object initialized in the Looper constructor. The handler sends the message to MessageQueue.
Queue. EnqueueMessage (MSG, uptimeMillis); Source:
boolean enqueueMessage(Message msg, long when) { if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w(TAG, e.getMessage(), e); msg.recycle(); return false; } msg.markInUse(); msg.when = when; Message p = mMessages; boolean needWake; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. msg.next = p; mMessages = msg; needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake // up the event queue unless there is a barrier at the head of the queue // and the message is the earliest asynchronous message in the queue. needWake = mBlocked && p.target == null && msg.isAsynchronous(); Message prev; for (;;) { prev = p; p = p.next; if (p == null || when < p.when) { break; } if (needWake && p.isAsynchronous()) { needWake = false; } } msg.next = p; // invariant: p == prev.next prev.next = msg; } // We can assume mPtr ! = 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; }Copy the code
MessageQueue has a list of messages stored in it, but it is not. MessageQueue implements a one-way linked list with a Message object (Next) member variable, and an mMessages object represents the current Message to be processed. Let’s look at the code above, line 20, first Message p = mMessages; To be processed in the current message assigned to p, followed by a long to judge the if (p = = null | | the when = = 0 | | the when < p.w hen), according to the judgment that, there are two situations will perform the if statement inside: 1. When the application calls sendMessage for the first time, if the current message to be processed is null, p is empty, and sendMessage will be executed. 2. When of the current incoming MSG is smaller than when of the current MSG to be processed, and the current incoming message is processed before the current mMessages to be processed, so the incoming MSG is assigned as mMessages, and the next message to be processed is the original message to be processed.
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
}
Copy the code
When multiple sendMessage methods are executed and the time of the message is larger than the time of the current message to be processed, the linked list is moved and inserted into the appropriate position according to the time sequence, calling the following code:
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
Copy the code
MessageQueue uses MessageQueue to form a one-way linked list of multiple messages by time. The first message is sent first, and MessageQueue uses MessageQueue to specify the next message object.
4) Looper object, which takes the current Message to be processed. The looper.loop () method is used to retrieve the current Message from MessageQueue. You can see why the child thread that initializes the Handler ends up calling looper.loop (), which is essentially the loop that gets the Message method, called by default in the main thread.
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; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging ! = null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging ! = null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident ! = newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); }}Copy the code
As you can see, line 13 is still in an endless loop, executing the queue.next() method, the next() method that actually does the message out of the queue. Getting the current mMessages object to be processed and making the next message mMessages is no more difficult than a linked list operation. Loop () method of line 27, after the Message is available, call the MSG. Target. DispatchMessage (MSG) method, MSG is the Message object, target, what is it? Target is the Handler object that sends the Message. Call Message Message = handler1.obtainMessage(); When, the assignment is performed:
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
Copy the code
The dispatchMessage() method in this Handler is dispatchMessage().
public void dispatchMessage(Message msg) { if (msg.callback ! = null) { handleCallback(msg); } else { if (mCallback ! = null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}Copy the code
If msg.callback and mCallback are null, the handleMessage (MSG) method will be called directly, and the handleMessage method will update the UI. When msg.callback, mCallback is not null? Msg. callback is not null, but can be assigned to any of the following methods:
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
Copy the code
Here is my call method:
package com.example.test.myapplication; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; public class MainActivity extends Activity { private Handler handler1 = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { Message message = Message.obtain(handler1, new Runnable() { @Override public void run() { System.out.println("handler2 Thread========="+Thread.currentThread().getName()); }}); message.arg1 = 1; handler1.sendMessage(message); } }).start(); }}Copy the code
As you can see from the printed information, the run method is actually the main thread. There are many ways to run, which are described below.
Other: 4.
Callback callback callback callback callback callback callback callback callback
The post() method of Handler, the Post () method of View, and the runOnUiThread() method of Activity can all be forwarded to the main thread in the child thread to update the UI. 1) Handler post() source code
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
Copy the code
The sendMessageDelayed method is still called, and Runnable is wrapped as a Message, followed by the linked list, the process is the same as before.
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
Copy the code
Just at the end, msg.callback! = null, it will call handleCallback(MSG);
private static void handleCallback(Message message) {
message.callback.run();
}
Copy the code
This way, the run method is called, isn’t it simple?
2) View post() source View class, post method:
public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo ! = null) { return attachInfo.mHandler.post(action); } // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); return true; }Copy the code
Mhandler. post(action).
3) Activity runOnUiThread() source code
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
Copy the code
If the current thread is not equal to the UI thread, the mHandler.post(action) method is still executed, and if it is on the main thread, of course, the direct interface callback calls the run method.
5. Conclusion:
So whenever the seed thread goes to the main thread to refresh the UI, the principle behind it is the same. Once you’ve figured out one, you’ve figured it out. After reading this article, you can ask the child thread to update the main thread.
Wechat search [programmer xiao an] “interview series (java&andriod)” article will be published in the public account.