1. 为什么Android会设计Handler去更新UI
Handler根本上是为了解决多线程之间引发的并发问题,在ActivityThread中,要是有多个子线程在没有加锁的情况下更新UI,有可能引发UI显示错乱的现象,但要是对更新UI的操作进行类似synchronized加锁机制的话,会造成性能下降,而Handler允许多线程向一个MessageQueue中押入Message,在UIThread中通过轮询的方式取出Message,并进行对应的处理,这样就可以避免UI显示错乱的问题,又不会降低性能。
2.Handler的内部机制---Looper,MessageQueue
(1).Looper:在一个Handler对象被创建时,内部自动关联了一个Looper对象,而Looper的内部会包含一个消息队列(MessageQueue对象),所有的给该Handler对象发送的Message,都会被加入到这个MessageQueue,
在Looper被创建之后,会调用Looper.loop()方法,looper()方法内部开始了一个for(;;)的死循环,不断的从MessageQueue中读取消息,如果取到Message,则处理消息,没有消息则处于阻塞状态。
(2).MessageQueue,消息队列为存放消息的容器
首先,查看ActivityThread.java源码,在ActivityThread的main()方法中可以看到这么一行代码:
Looper.prepareMainLooper();
打开这个prepareMainLooper()方法,在Looper的源码中可以看到:
public static void prepareMainLooper() { prepare(); setMainLooper(myLooper()); myLooper().mQueue.mQuitAllowed = false; }
而prepare()的源码:
public static void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); }
可以看出,prepare()方法中,首先是从sThreadLocal中通过get()判断是否为null,若为null,则会new一个Looper对象,并存储在sThreadLocal中,查看new Looper()方法的源码:
private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread(); }
在Looper()的构造函数中,会创建一个消息队列MessageQueue,并将mThread指定为Thread.currentThread(),即Handler被new的线程,当Handler在UIThread中创建的时候,该mThread就是UIThread,那么为何此处要获得是在那个Thread中创建该Handler呢,首先让我们看看ThreadLocal这个数据类型,ThreadLocal用于存放与线程相关的变量信息,我们可以看到在Looper()构造函数中有:
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
此处创建了一个存放Looper的ThreadLocal对象,在上面的prepare()中可以看到,new出来的Looper()通过set()方法存放在ThreadLocal中,看看ThreadLocal的set()方法:
public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); }
ThreadLocal的get()方法如下:
public T get() { // Optimized for the fast path. Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); }
可以看得出来,ThreadLocal的set()方法中,存储变量value的values是通过currentThread唯一创建的,不同的Thread初始化获得的values不同,而ThreadLocal的get()方法中,也是首先通过currentThread获得currentThread对应的values,从而获得value,这样处理,是为了在不同的Thread中通过ThreadLocal获得相对于的已经存入T hreadLocal的变量信息,ThreadLocal就是为了在多线程的情况下,为不同的线程保存相同类型的,不同的数据对象。
从Looper()的构造函数中看到,MessageQueue是在Looper被创建的时候创建的,用于接收发送给对应Handler对象的Message对象。
下面看一下Handler被new创建的源码:
public Handler() { 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 that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = null; }
可以看到有两行重要的代码:
mLooper = Looper.myLooper(); mQueue = mLooper.mQueue;
其中mLooper = Looper.myLooper()方法的源码:
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static Looper myLooper() { return sThreadLocal.get(); }
是通过sThreadLocal变量get()到的,而ThreadLocal的get方法会拿到currentThread对应的Looper对象,这就可以与前面的代码联系起来了。ActivityThread通过执行prepare()方法为UIThread唯一创建了Looper对象和接受Message的消息队列,并保存在了UIThread的ThreadLocal对象中,而在UIThread中创建Handler的时候,拿到的Looper正是之前ActivityThread中创建的Looper,而mQueue即是对应的消息队列,两处的Looper指向相同,两处的MessageQueue指向也相同。
Handler在被创建的时候拿到消息队列,便可以通过如handleMessage()方法向该消息队列中添加请求,而在ActivityThread创建Message Queue的时候,就已经在主线程中通过Looper.loop()的方法开启了一个for死循环来轮询Message,这样就把UIThread中的Looper和Handler相关联起来了,以后在子线程中调用 handler.sendMessage()方法的时候,其实质就是将Message加载到了UIThread的MessageQueue中,而在UIThread中已经有一个死循环轮询并在UIThread中处理Message了。
下面我们跟踪一下子线程中的handler.sendMessage(0x1)方法,一层层的最终到了boolean sendMessageAtTime(Message, long)方法:
public boolean sendMessageAtTime(Message msg, long uptimeMillis) { boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { msg.target = this; sent = queue.enqueueMessage(msg, uptimeMillis); } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); } return sent; }
从上面可以看到,sendMessage方法最终是在mQueue中通过enqueueMessage()方法,将Message压入到mQueue中。
下面看看Looper.loop()方法:
public static void loop() { Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } 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(); while (true) { Message msg = queue.next(); // might block if (msg != null) { if (msg.target == null) { // No target is a magic identifier for the quit message. return; } long wallStart = 0; long threadStart = 0; // 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); wallStart = SystemClock.currentTimeMicro(); threadStart = SystemClock.currentThreadTimeMicro(); } msg.target.dispatchMessage(msg); if (logging != null) { long wallTime = SystemClock.currentTimeMicro() - wallStart; long threadTime = SystemClock.currentThreadTimeMicro() - threadStart; logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); if (logging instanceof Profiler) { ((Profiler) logging).profile(msg, wallStart, wallTime, threadStart, threadTime); } } // 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.recycle(); } } }
值得注意的是,loop()中首先会通过myLooper()方法获得Looper对象,而此Looper对象,是在sThreadLocal中get到的,与Handler创建时的Looper对象一样,都指向了ActivityThread中创建的Looper对象,同样对应的MessageQueue也是同一个;在loop()方法中会通过一个for(;;)死循环通过mQueue.next()取得Message,当Message对象不为null,则通过msg.target.dispatchMessage(msg)去处理数据,否则处于阻塞状态。
[注意]其中msg.target为当前Message对应的Handler。
而Handler的dispatchMessage(Message)方法:
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
根据情况分别调用mCallback.handleMessage(msg)方法和handleMessage(msg)方法。
[总结]
UIThread在被初始化创建的时候,会首先通过prepare()方法为当前线程创建一个Looper对象,并存储在当前线程的ThreadLocal中,Looper的创建,其内部会自动创建一个消息队列(MessageQueue)的对象mQueue,在UIThread中创建一个Handler对象uiHandler的时候,uiHandler会从当前线程(UIThread)的ThreadLocal中拿到Looper对象,从而获得ActivityThread为UIThread创建的MessageQueue对象mQueue,当子线程通过uiHandler发送Message的时候,会首先将Message的target指向当前的uiHandler,并将此Message对象压入到mQueue中,而在ActivityThread创建Looper成功之后,便会通过Looper.loop()方法以死循环的方式轮询mQueue中的消息,当获得一个不为null的Message对象时,就会将该Message发送给Message对应的target(target为Handler对象)处理,target回调我们自定义实现的Callback.handleMessage(msg)和handleMessage(msg)方法处理消息。