Android Handler源码浅析
相关对象
- Handler可以看作CEO,负责消息的处理和发送,Handler发送消息给MessageQueue,,然后Looper取出其中的消息给Handler。
- Looper,Handler的管家,可以看作秘书,负责管理MessageQueue,她会不断的从MessageQueue中取出消息,交给Handler处理。
- MessageQueue是存放消息的队列,负责存放Handler发来的消息。
Looper部分源码讲解
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));
}
static final ThreadLocal sThreadLocal = new ThreadLocal();
Looper实例化是在Looper.prepare()方法中。
小插曲
ThreadLocal是一个和多线程并发相关的类。当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal维护了一个Map集合,其中键为当前线程,值就是不同线程的变量。
ThreadLocal中的方法不多。
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
set方法很容易理解,首先获取当前线程,再用当前线程当键来获取map集合,如果map为空,则重新创建一个map并设置值进去,否则的话,直接设置值进去。get同理。
回到主题。所以Looper的prepare只能获取当前线程的Looper对象,并且如果已经存在一个Looper对象则会报异常,所以这也说明了一个线程中只能存在一个Looper对象。并且prepare只能调用一次。
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
public static void loop() {
final Looper me = myLooper();
//此处只展示了关键代码
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 {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
Looper中创建了一个MessageQueue,并且在Looper的loop方法中,开启无限循环去遍历MessageQueue,如果消息不为空,则将消息发送给Handler。这个msg.target其实就是Handler对象。
我们一般都会重写Handler的handleMessage方法,其中缘由也在源码里。
我们不管sendEmptyMessage也好,postDelayed也罢,最终都会调用到一个方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
msg.target = this。这句话中的this就是Handler对象。
同时把消息放入MessageQueue中。
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
在Looper的loop方法中,她把消息用dispatchMessage传了过来。在dispatchMessage中。优先级有三种。优先级最高的是message自己的callback,然后是Handler的callback,当两者都没有的时候,就由我们重写来处理。这也是我们最常用的一种方法。
那么Looper是什么时候启动的呢?
答案在ActivityThread中。
public static void main(String[] args) {
//部分代码
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.prepare()等价。这个ActivityThread也就是我们所说的UI线程,Looper在UI线程启动的时候,就一直在后台默默工作了。
思考,Handler怎么做到线程切换处理对象的?
因为不同线程共享内存,Handler在A线程发送了一个消息,然后主线程的Looper在主线程把消息取了出来,同时交给了Handler,所以Handler一取一存就做到了线程切换。
小结
其实看源码本没有那么复杂,有时候根本不需要抽丝剥茧一条条代码看,代码量很多,难度确实是有的。但是我们只要有一个目标,有一条主线,只寻找最关键的代码,就能很快的理清它的脉络。