面试常客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();