A good memory is better than a bad pen, today to analyze the Handler source code implementation
Handler mechanism is the basis of Android system, is the basis of switching between multiple threads. Let’s look at the source code implementation of Handler.
Handler Message mechanism has four kind of collaboration, Handler, MessageQueue, stars, and the Message Handler: get messages, send messages, and processing the messages class MessageQueue: Message queue, fifo stars: Loop and distribute messages: The Message entity class that distributes and processes messages
Here’s how it works: The Looper class has an infinite loop that keeps pulling messages from the MessageQueue queue and distributing them to handlers for processing
Let’s look at sending a message in the child thread, to update in the main thread, so we print a sentence in the main thread.
Step 1: Inside the MainActivity there is a property uiHandler as follows:
Handler uiHandler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); If (msg.what == 100){log.d ("TAG"," thread ") 1 msg.what=" + msg.obj=" + msg.obj ()); }else if(MSG. What == 200){log.d ("TAG"," thread 2 MSG. What + "MSG. }}};Copy the code
Create a Handler instance that overrides the handleMessage method. According to the what identifier in message to distinguish the data sent by different threads and print
Step 2: Open two threads in the button click event, use uiHandler in each thread to fetch the message and send the message. The following
findViewById(R.id.tv_hello).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Thread1 new Thread(new Runnable() {@override public void run() {uihandler.obtainMessage (); message.what = 100; message.obj = "hello,world"; //2 sendMessage uiHandler. SendMessage (message); } }).start(); Thread 2 new Thread(new Runnable() {@override public void run() {uihandler.obtainMessage (); message.what = 200; message.obj = "hello,android"; //2 sendMessage uiHandler. SendMessage (message); } }).start(); }});Copy the code
It’s very simple to use, it’s two steps to send data from the child thread to the main thread and process it in the main thread. Let’s first analyze the source code of Handler
Handler source code analysis
Handler constructor
public Handler() {
this(null, false);
}
Copy the code
The constructor for the second argument is called, as follows
Public Handler(Callback Callback, Boolean async) {//FIND_POTENTIAL_LEAKS is false, If (FIND_POTENTIAL_LEAKS) {final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }Copy the code
MLooper = looper.mylooper (); Call Looper static method to get a Looper if mLooper == null, Can’t create handler inside Thread “+ thread.currentThread () +” that has not called looper.prepare ()”; New Handler() will raise this exception if our thread does not have a looper. You must first call looper.prepare (), which will become clear when we look at the source code for Looper.
Next, we assign the mQueue in mLooper to the mQueue in Handler. Callback is the outgoing value, null, and we hold a Looper variable in our Handler, a MessageQueue MessageQueue.
Next is Message Message = uiHandler.obtainMessage();
ObtainMessage () ¶
Public final Message obtainMessage() {return message.obtain (this); return message.obtain (this); }Copy the code
Message.obtain(this) is called again; Method, source code as follows:
Public static Message obtain(Handler h) {public static Message obtain(Handler h) { // the target of this message is the target of this message. Return m; }Copy the code
This way, the Handler instance is stored in the message. Let’s take a quick look at how the obtain() method obtains information. The following
Public static Message obtain() {// synchronized (sPoolSync) {// synchronized (sPoolSync) {//sPool is a Message type, and the static variable if (sPool! M = null) {// It is a single linked list, and sPool points to the next Message. sPool = m.next; m.next = null; m.flags = 0; // clear in-use flag sPoolSize--; return m; }} // If sPool is empty, simply new a return new Message(); }Copy the code
Obtain () information is the share design pattern, which in plain English is: if there is one in the pool, return one from the pool, and if there is none, create a new one, add it to the pool, and return it.
Using this pattern can save you too much object creation. Reuse free objects to save memory.
Uihandler. sendMessage(message); The source code is as follows:
public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}
Copy the code
SendMessageDelayed (MSG, 0
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
Copy the code
SendMessageAtTime ()
Public Boolean sendMessageAtTime(Message MSG, long uptimeMillis) { Looper. Get 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
Call enqueueMessage ()
Private Boolean enqueueMessage(MessageQueue queue, Message MSG, long uptimeMillis) { If we send a Message that is not uiHandler.obtainMessage(), but directly new Message(), then target is null. MSG. Target = this; // mAsynchronous defaults to false and will not walk this if (mAsynchronous) {msg.setasynchronous (true); } return queue.enqueueMessage(msg, uptimeMillis); }Copy the code
Queue. EnqueueMessage (MSG, uptimeMillis);
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
Enqueue means to queue, to join a queue. So enqueueMessage() inserts the message into the single linked list. The source code above shows that the MSG is inserted into the first position in the single linked list in chronological order. Then we need to fetch the MSG from the message queue and divide it into processing. The looper.loop () method is called.
Looper.loop() I’ve simplified the source of looper.loop () to leave the main flow behind:
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; for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } msg.target.dispatchMessage(msg); msg.recycleUnchecked(); }}Copy the code
As you can see, the loop () method is kept in an infinite loop took out the next message from the queue Then call MSG. Target. DispatchMessage (MSG), we analyzed above, the Message of the target to save is to send the Handler instance, And this, in our demo, is uiHandler object.
In other words, it takes a message from the message queue and sends it to the Handler’s dispatchMessage() method.
Handler’s dispatchMessage() method is as follows:
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 a message is sent to dispatchMessage(), the callback is null. If not, the callback is handled by the message’s handleCallback() method
If the Handler returns true, the message will not be distributed. If the Handler returns false, the message will not be distributed
3 Passes the handleMessage() method to Handler.
There are three levels of interception. Note that there are many plugins that replace an activity by hooking the Handler instance of the activity to a variable associated with the ActivityThread. MCallback is not null and returns false. In this way, the normal flow of the system is not affected, and the purpose of interception can be achieved. Say more.
Handler handler handler handler handler handler handler handler handler handler handler handler handler handler handler handler handler handler handler handler handler handler handler handler handler
Looper source code analysis
As we know, the entry point to the APP process — our APP — is the activityThread.main () function. I need to make up for this unfamiliar part privately.
The source code for ActivityThread.main() has also been simplified as follows:
Files in the/frameworks/base/core/Java/android/app/ActivityThread. Java
Public static void main(String[] args) {//1 Create a looper looper. PrepareMainLooper (); //2 Create an ActivityThread instance and call attach(). ActivityThread = new ActivityThread(); thread.attach(false, startSeq); //3 message loop looper.loop (); }Copy the code
As you can see, the first line of the main thread is to create a looper and call looper.loop () to loop the message, because a thread must have a Looper to loop the message, and to continuously pull messages from the message queue, distribute messages, and process messages. When there is no message, it blocks and waits for the message to arrive and be processed. In this way, our app runs in this message-driven way.
PrepareMainLooper () looper.prepareMainLooper (
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
Copy the code
Prepare (false) = prepare(false);
Private static void prepare(Boolean quitAllowed) {//1 check whether looper exists in the current thread. Only one looper can exist in a thread if (sthreadLocal.get ()! = null) { throw new RuntimeException("Only one Looper may be created per thread"); } //2 Create a Looper and place it in sthreadLocal. set(new Looper(quitAllowed)); }Copy the code
Static final ThreadLocal
sThreadLocal = new ThreadLocal
();
Is a static variable. There is only one sThreadLocal in the entire APP process. SThreadLocal is unique to the thread, and each thread calls sThreadLocal to save. The key is the sThreadLocal, but the mapped array is unique to each thread. This ensures that each thread has its own copy of the data stored in the sThreadLocal.
The Looper class is thread related, so let’s look at the Looper class definition.
/**
* Class used to run a message loop for a thread. Threads by default do
* not have a message loop associated with them; to create one, call
* {@link #prepare} in the thread that is to run the loop, and then
* {@link #loop} to have it process messages until the loop is stopped.
*
* <p>Most interaction with a message loop is through the
* {@link Handler} class.
*
* <p>This is a typical example of the implementation of a Looper thread,
* using the separation of {@link #prepare} and {@link #loop} to create an
* initial Handler to communicate with the Looper.
*
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }</pre>
*/
public final class Looper {
.......
}
Copy the code
Let’s look at the comments above
* <pre>
* class LooperThread extends Thread {
* public Handler mHandler;
*
* public void run() {
* Looper.prepare();
*
* mHandler = new Handler() {
* public void handleMessage(Message msg) {
* // process incoming messages here
* }
* };
*
* Looper.loop();
* }
* }</pre>
Copy the code
This is the classic use of Looper. You can call looper.prepare () at the start of a thread; Looper.loop() is then called at the end; A Looper thread can be used to switch threads easily by passing handlers from other threads to the message loop. In the next section, we will design a Looper thread to do some background tasks.
Handler message mechanism source code analysis so far