在项目中经常用Handler,然而一直没有深究其原理,今天就来探究一下这个熟悉的陌生人。
提到Handler
就需要提到Looper
和Message
了,这三者就像刘关张一样,是拜把子的兄弟。
先介绍一下他们三个:
Handler
: 句柄,负责向MesageQueue消息队列中发消息和处理消息
Looper
: 循环器,不断从MessageQueue消息队列中取消息
Message
: 消息
接下来看看Handler的原理,我们知道Android的应用是由事件驱动运行的。Java程序的入口函数是main()
函数,Android应用也是一样,Android应用启动也是从main()
函数开始,这个函数在ActivityThread
中。
我们来看看main()
方法的内容:
...上面省略
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
看到有一行是Looper.prepareMainLooper()
从命名看出它的作用因该是准备一个主Looper,我们来看看prepareMainLooper()
的源码:
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
接下来看prepare(false)
的源码:
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));
}
形参看名字就知道是,是否允许退出,很明显这个不允许的。
这里有个sThreadLocal
变量,我们来看看它是干什么用的
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
在成员变量中找到了它的定义,发现这是ThreadLocal
类型的变量,那么,ThreadLocal
是干什么的呢?
看到它的get()
set()
方法,感觉它应该是一个容器。接下来我们来验证一下,先看看set()
方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
这里有一个ThreadLocalMap
我们查看这个类的注释发现有这么一句:
ThreadLocalMap is a customized hash map suitable only for maintaining thread local values.
说明这是一个自定义的HashMap
容器(改进了HashMap的算法,以使得排列更加均匀)
那么就可以很清晰看到这里是将ThreadLocal
作为键,Looper
作为值存储起来。从而得出结论,ThreadLocal
的作用就是将Looper
与当前线程(主线程)关联起来,并且保证Looper
的唯一(线程安全)。
这样looper()
方法就看完了,塔的作用就是创建一个Looper
并将其与主线程关联。
接下来看看最后一行的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;
···
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
···
try {
msg.target.dispatchMessage(msg);
} finally {
···
}
···
msg.recycleUnchecked();
}
}
可以看到,这里先是获取了一个Looper
对象,这里的myLooper()
的代码很简单,就是调用了ThreadLocal
的get()
方法,获取Looper
对象,如果这里报了异常,也就是说之前没有调用Looper.prepare()
方法。这里就不多说了
接下来就是获取消息队列。
然后就是一个死循环,遍历消息队列中的消息。queue.next()
也就是一直获取下一个消息。如果消息队列中没有消息则退出。
接下来看msg.target.dispatchMessage(msg);
,看到后面的dispatchMessage(msg)
发现这个方法有点熟悉,这不是Handler
里面的方法么? 我们来看看Message
的target
究竟是什么👻?
我们在属性中看到了它: Handler target;
,这就很清晰了。
接下来我们看看Handler的dispatchMessage(msg)
方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
首先看这个方法的名字,可以知道它的作用是分发消息
先看msg.callback
,这个callback
又是什么呢?通过查看Message
的属性,看到Runnable callback;
,那么msg.callback
就是一个Runnable
类型的变量了。
接下来看handleCallback(msg);
private static void handleCallback(Message message) {
message.callback.run();
}
发现这里会调用Runnable
的run()
方法。这个时候大脑灵光一闪,我们平常通过Handler
发消息时使用的handler.post(Runnable)
最终是不是通过这里回调的run()
方法呢?答案是肯定的,后面再分析
继续看,mCallback
是一个接口,里面有一个抽象方法,如果我们在创建Handler的时候实现了这个接口,就会在这里回调了。
比如:
Handler dHandler = new Handler(new Handler.Callback() {
@Override public boolean handleMessage(Message msg) {
return false;
}
});
继续看,如果handleMessage(msg);
,这里就不用解释了。
举例:
Handler dHandler = new Handler() {
@Override public void handleMessage(Message msg) {
}
};
这样我们就能理解loop()
方法了,就是不断的循环,从消息队列中取出消息,通过回调让Handler处理消息。
loop()
方法最后一行msg.recycleUnchecked();
这个的意思就是回收消息。
前面说了,Android应用是由事件驱动的,应用在运行时不断的从消息队列中拿消息处理消息,那么我们如果每发一个消息就new一个对象显然是不行的,因此,在MessageQueue
中有一个消息池,消息是用链表方式存放的,消息池中的消息不断被使用-回收。
再来看看我们平常使用Handler
发消息的用法:
Handler handler = new Handler();
Runnable run = new Runnable() {
@Override public void run() {
}
};
handler.post(run);
通过查找post()
方法的源码,发现里面调用的是sendMessageDelayed(getPostMessage(r), 0);
方法,我们来看看它的源码:
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
发现这里面调用了另一个方法,继续追溯到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);
}
我们来看看enqueueMessage(queue, msg, uptimeMillis)
方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
}
这里可以看到最后是插入消息,将Handler
发的消息插入到消息队列中并且会将发消息的Handler
绑定到Message
中。在将消息插入消息队列时会附加一个时间,而消息队列里面的消息就是按这个时间来排序。前面说了,应用启动了之后会不断从消息队列中取消息,而我们用Handler发送消息的时候,消息会被插入到消息队列中,然后Looper
一直从消息队列中取消息,并将Message
通过其绑定的Handler
回调处理。
网友评论