Handler,Looper,Message
Longer length
Handler.
public class MainActivity extends BaseActivity { private Handler mHandler0; private Handler mHandler1; @Override protected void onCreate(Bundle savedInstanceState) { ...... mHandler0 = new Handler(); new Thread(new Runnable() { @Override public void run() { mHandler1 = new Handler(); } }).start(); }
At this time, you will find that the Handler object created in the thread will crash. The error message is: can't create Handler inside thread that has not called looper prepare()
According to the prompt, we did not use looper prepare(); We add this line of code to debug:
new Thread(new Runnable(){ @override public void run(){ Looper.prepare(); mHandler1 = new Handler(); } }).start();
We found that we didn't collapse. At this point, there will be a question, why does this problem occur? Let's follow the source code, starting with the constructor:
public Handler() { this(null, false);}
The constructor simply calls this function, and we continue to track this function
public Handler(Callback callback, boolean async) { ...... mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
It can be seen that the second exception is thrown when mloop is null in this function. When will it be empty? Continue to track myloop();
public static Looper myLooper() { return sThreadLocal.get(); }
We will have questions in this method. What is sThreadLocal? What's the function. Let's first look at the declaration of sThreadLocal in the class:
static final ThreadLocat<Looper> sThreadLocal = new ThreadLocal<Looper> ();
It can be simply regarded as a set of loopers. If there is a Looper object in the sThreadLocal variable, it can be got (), otherwise it will return null. Tracking this way, we can probably guess Looper What did prepare () do? Set sThreadLocal; Let's look at the source code:
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)); }
Here's the end of 'why prepare and create again'. But our curiosity will ask: why doesn't the main thread need to be created after perpare?
Why don't we prepare the main thread before creating it
The reason why the main thread does not need to prepare is that it has dealt with this problem in advance. Let's look at the main() method in ActivityThread:
public static void main(String[] args) { ...... Looper.prepareMainLooper(); ...... if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
In this method, we can see that looper prepareMainLooper(); What does this method do? We continue to track:
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } } }
We see that prepare() is executed in this method.
Conclusion: in the main thread, google has created Looper for us, but in the sub thread, it does not automatically create Looper for us, so we need to call Looper ourselves prepare().
Send message
Next, let's learn how to send messages. The process is to create a Message - > set the value to the Message object - > send with the help of the Handler.
new Thread(new Runnable() { @Override public void run() { Message msg = Message.obtain(); msg.arg1 = 1; Bundle bundle = new Bundle(); bundle.putString("key","frenk"); msg.setData(bundle); mHandler0.sendMessage(msg); } }).start();
After sending, we can use the handleMessage() method in the Handler to take out this Message for use. But what is the specific internal process? Let's analyze the source code together. Start with the send method sendMessage(). The source code is too long. Some methods will directly introduce the function. Those who are interested can see the source code themselves
Queue operation
public final boolean sendMessage(Message msg){ return sendMessageDelayed(msg, 0);}
public final boolean sendMessageDelayed(Message msg, long delayMillis){ if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
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); }
According to the analysis of sendMessageAtTime(), the Message object creates a Message queue MessageQueue, which is assigned by mQueue. In the source code, mQueue = mLooper mQueue, and mloop is a Looper object. It is introduced when the Handler is introduced at the top. Each thread has only one Looper. Therefore, the corresponding Looper corresponds to the corresponding MessageQueue. Then enqueueMessage(queue, msg, uptimeMillis) is the queue operation.
private boolean enqueueMessage(MessageQueue queue, Message msg, long upt imeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
boolean enqueueMessage(Message msg, long when) { ...... synchronized (this) { if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", 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) { msg.next = p; mMessages = msg; needWake = mBlocked; } else { 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; } if (needWake) { nativeWake(mPtr); } } return true; }
Observe the code in the method. The queue is sorted according to the time when, which is the parameter we passed all the way, and then call MSG according to the time order Next to specify the next Message to be processed. If it is queued through enqueueMessage(queue, msg, 0) in sendMessageAtFrontOfQueue(), but it does not set the delay time, the Message will be directly added to the header. When you join the team, you'll know almost. The next step is out of the team operation.
Out of line operation
Our queue MessageQueue object is assigned in the Looper, so we can directly find the queue operation in the Looper class. Take a look at Looper Loop() method:
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't ca lled on this thread."); } final MessageQueue queue = me.mQueue; Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); if (msg == null) {return; } ...... msg.target.dispatchMessage(msg); ...... msg.recycleUnchecked(); } }
There are too many codes. Only the important analysis is selected. After entering the for loop, the message MSG is continuously taken from the MessageQueue object. next() is the queue outgoing method. The code of next() method is a little long, so it will not be posted here. The main logic of next is to judge whether there are pending mMessages messages in the current MessageQueue. If so, queue this message, and then make the next message become mMessages. Otherwise, it will enter the blocking state until a new message enters the queue and wakes up. Looking back at the loop method, MSG will be executed after executing the next() method target. dispatchMessage(msg);, MSG Target is the Handler object. Finally, take a look at the dispatchMessage() method:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
If mCallback is not null, mCallback. Is called handleMessage(), otherwise, call handleMessage() and pass the message as a parameter. In this way, we can see why we should use handlemessage () to capture the information we passed in the past.
According to the above understanding, it is not difficult to write the thread of asynchronous message processing mechanism.
new Thread(new Runnable(){ @override public void run(){ Looper.prepare(); mHandler1 = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); //Processing messages } }; Looper.loop(); } }).start();
summary
Looper is responsible for creating a message queue MessageQueue object, and then entering the dead loop to continuously fetch messages. These messages are created and processed by one or more handlers.