面试常客Handler,几乎所有的面试官都会问Handler的运行机制、四个组成部分以及其源码。今天我从源码的角度来分析下handler内部的机制原理。
Handler由四部分组成,Handler、 Looper、 Message、 MessageQueue
我们先来看Looer。
Looper
Looper中有两个主要的方法,looper.prepare() 以及looper.loop()。
looper.prepare():
public static void prepare() { prepare(true); } 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)); }
我们看到prepare方法中,先有一个sThreadLocal对象,从这个对象中我们获取只先进行了一个非空判断,如果此对象不为空,则抛出异常。然后如果为空,则往sThreadLocal对象中set一个new出来的Looper对象。
先来解释下sThreadLocal这个对象,其实这个对象就是一个ThreadLocal类,可以在线程中存储对象,我们通过这个类将Looper这个对象存储进去。
其次来解释下这个非空判断。如果从线程中取出这个对象不为空,则抛出异常,这一重判断表明了looper的一个重要原理,即一个线程当中只能拥有一个looper对象,如果有多个则会抛出异常。
我们再来看下new Looper()对象的构造方法源码
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
从源码得知,在Looper构造方法中实例化了一个MessageQueue对象。
到此,prepare()这个方法讲解完毕。
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 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } 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(); } }
我们可以看到,在loop()方法中首先调用了一个myLoop()方法,我们看下这个方法的源码
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
从源码可以看出,myLooper这个方法中从ThreadLocal线程拿到了之前我们在prepare()set进去的looper对象。所以loop()方法首先调用myLooper()方法获取looper对象,之后又做了一个为空判断,如果为空抛出异常。由此句代码标明,loop()方法必须在prepare()方法之后才能调用,因为必须要先用prepare()方法创建一个Looper对象,不然会抛出异常。
之后通过me.mQueue拿到一个MessageQueue对象queue,之后用for进行了死循环,通过queue.next()不断从消息队列中拿出Message消息,如果消息为空则跳出循环,如果不为空则调用msg.target.dispatchMessage()方法,把这个获取出来的消息交给这个方法去处理。其实这个方法就是handler,这个我们后面讲到了handler再说,这里不展开。最后调用msg.recycleUnchecked();消息回收。
至此Looper两个主要的方法讲完。
我们来总结下:
首先调用looper.prepare()方法来创建一个Looper对象,并将这个对象存放进ThreadLocal线程当中去,并调用if判断,确保线程中有且仅有一个loopr对象。
之后调用looper.loop()方法先取得存进线程中的looper对象,之后通过looper对象获取MessageQueue消息队列对象queue.然后进行for循环,注意,这里是一个死循环,从消息队列中不断next()拿出Message消息,并且将消息交给msg.target.dispathcMessage去处理,最后进行回收处理。
Handler
我们在使用handler的时候,一般会先去new一个handler对象,所以我们先看下handler的构造方法。
public Handler(Callback callback, boolean async) { 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 = callback; mAsynchronous = async; }
从构造方法可以看到,通过myLooper()方法获取到了looper对象,之后进行非空判断,确保在使用的时候有这个looper对象。之后通过这个looper对象获取MessageQueue对象。这样确保了这个handler和之前调用Looper.prepare()中new出来的MessageQueue为同一个消息队列。
之后我们看下 sendMessage() 方法。
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); }
public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
最后发现,我们最后调用的是sendMessageAtTime()方法。我们来看下这个方法:
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); }
这个方法最后是将获取到的MessageQueue对象存放到了enqueueMessage()方法中去,并将他返回。
我们再来看enqueueMessage()这个方法里面做了什么事情
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
这里注意到,一开始将this对象赋值给了msg.target。还记得之前在分析looper.loop()源码的时候,之后将MessageQueue取出来的消息交给了一个叫msg.target.dispathcMessage去处理吗。其实这里的this就是handler对象,等于说把handler对象赋值给了msg.target对象去,最后将这个对象添加到消息队列的enqueueMessage()方法中去。等于说,handler发送出来的消息最后全都放到了消息队列当中去。
我们再来看下 dispatchMessage() 这个方法:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
我们看到最后调用了一个我们常常在使用handler的时候覆写的一个方法handleMessage()
我们来看下这个handleMessage()方法源码
public void handleMessage(Message msg) { }
发现里面是空的,这代表什么呐?这代表这个方法Android什么也没帮我们干,我们可以自己根据自己的业务逻辑来写入相应的逻辑代码。这也就是平时我们写代码的时候要覆写这个方法,并且在这个方法中写业务代码的原因。
至此handler的源码也看完了,我们来总结下:
我们在使用handler的时候首先会new一个handler对象,handler构造方法里面会从mylooper()方法中获取一个looper对象,并且用looper对象去调用MessageQueue来获取同一个线程中的消息队列,确保Looper.prepare()中new的MessageQueue和handler中的消息队列为同一个。
之后通过sendMessage()方法发送消息。这个方法最终调用的是sendMessageAtTime()方法。这个方法最终是将handler发送的消息添加到消息队列MessageQueue当中去。最后调用dispatchMessage方法来处理这些消息。最终处理这些消息的业务代码放在了一个handMessage()空当中由程序员自行根据自身的业务逻辑去编写代码。
总结
Handler机制的原理如下:
先通过Looper.prepare()创建一个looper对象并将他添加进入ThreadLocal线程当中。通过if判断,确保一个线程中有且仅有一个looper对象。
之后通过Looper.loop()从ThreadLocal线程中获取looper对象,并且调用looper.queue对象获取到消息队列MessageQueue,利用死循环不断从消息队列中获取消息,交给msg.target.dispatchMessage去处理。
此时handler中,通过构造方法从线程中获取looper对象,并通过这个对象获取消息对应的消息队列,确保消息队列和之前的looper.loop()调用的是同一个。
之后sendMesage将发送的消息存放到消息队列MesageQueue中。然后在dispatchMessage中调用了handMessage()方法去处理MessageQueue中的消息。至此,Handler全部原理结束。
但是我们在使用Handler的时候,并没有使用loop.prepare()创建looper,也没有用looper.loop()来启动looper啊!其实Android的主线程中就已经有了一个looper对象,所以不需要我们自己去创建这个对象,如果是子线程中那就需要我们自己去创建处理了,代码如下:
new Thread() { private Handler handler; public void run() { Looper.prepare(); handler = new Handler() { public void handleMessage(android.os.Message msg) { Log.e("TAG",Thread.currentThread().getName()); }; }; Looper.loop();