【Handler】(sendMessage) 【Handler】(sendMessageDelayed) 【Handler】(sendMessageAtTime) 【Handler】(enqueueMessage) 【MessageQueue】(enqueueMessage) 消息入队 【Looper】(loop) —->【MessageQueue】(next)消息出队 【Handler】(dispatchMessage) 【Handler】(handleMessage) 这是Handler发送消息和最后接收到消息处理消息的整体流程。
Handler Looper MessageQueue之间的对象关系。
Handler中包含两个成员变量mQueue,mLooper。
MessageQueue mQueue Looper mLooper
在Handler初始化的时候(Handler的构造方法里)这个两个变量进行初始化。
mLooper = Looper.myLoop(); mQueue = mLooper.mQueue ;
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }mLooper是通过ThreadLocal的get方法拿到的。
ThreadLocal会通过set方法给当前线程关联一个Looper对象。
我们知道应用启动的入口是ActivityThread的main方法,其中:
先调用了Looper.prepareMainLooper();后调用了Looper.loop()方法。
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }在Looper.prepareMainLooper方法里调用了prepare方法;
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)); } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }Looper.prepare() 完成Looper对象的初始化,在Looper初始化的时候也初始化了mQueue,然后通过ThreadLocal的set方法关联了Looper,而在后面我们在主线程初始化Handler的时候,可以通过ThreadLocal的get方法直接拿到主线程的Looper,而Handler里面的mQueue就是Looper里的mQueue。
再看一下Looper.loop方法
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(); } }有个for循环,会通过mQueue.next方法去取消息,然后交给Handler的dispatchMessage方法,继而调用handleMessage方法处理消息。
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }其实简单总结一下就是 ,Handler发送消息就是讲消息压入MessageQueue队列。而对于主线程的looper,系统是帮我们默认调用了loop()方法来开启for循环取出消息,进而交给Handler的handle message方法去处理消息。
Handler实现的是线程间的通信,我们用的最多的就是在子线程里进行了耗时操作,完成后要对我们的UI进行更新。而Android的设计规范是不允许我们在子线程中更新ui的,Handler的消息机制就这样诞生了。
那么为什么不允许我们在子线程中更新UI呢?主要是我们的UI控件是线程不安全的,如果多个线程同时对UI进行并发操作,必定会带来各种问题。那么问什么我们的google工程师不把我们对UI控件的访问都设计成线程安全的呢?主要有两点:1.线程安全就必须要加锁机制,我们都知道锁机制是会阻塞线程的执行的,如果对我们的各种控件都进行加锁的话,势必访问UI的效率会大大降低,效率降低就会影响用户体验。2.如果加锁那么访问UI的逻辑就变的很复杂,代码量会增加很多。所以最简单高效的的方法就是用单线程来处理UI操作。通过Handler来切换一下UI访问的执行线程。
再补充一丢丢,我们如果在子线程中更新了UI,那么就会抛出异常,这个异常是ViewRootImpl的checkThread方法来抛出的,也就是这个方法会来验证你操作UI的线程是不是主线程。之前有面试官问到能不能在子线程里更新UI呢?为什么呢?正常来说当然不能,他可能问你的点就是你知不知道这个Exception是怎么抛出来的,是谁抛出来的。如果你有过这个疑问,看过这个源码你当然就知道是怎么回事了。如果再深入一点,ViewRootImpl是在哪里初始化的呢?如果我们在它初始化之前在子线程里访问UI,那么它就不会抛出异常了,因为ViewRootImpl没有被初始化,自然checkThread方法就不会被调用了,也就不抛异常了。那ViewRootImpl是在什么时候初始化的呢?这就涉及到了Activity的启动过程,各个生命周期方法的调用,我们自己写的contentView布局的加载,绘制流程。这些都是需要读源码的。读系统源码还是有很多好处的,我们应该多多读源码,多多学习下。
