Android Handler源码解析
大家都知道Handler是Android系统的一种异步机制,常用在子线程中更新UI线程,听说面试的时候HR经常会问“你分析过Handler的源码吗?”,博主出于这种不纯的动机分析了一下Handler的源码 ^_^(别鄙视我)。
首先,大家一般想要使用一个对象的时候用的最多的关键字是什么呢?对,就是new这个关键字,他创建了一个对象供我们使用,现在我们就从Handler的构造汉初开始分析。
代码块1
public Handler(Callback callback, boolean async) {
........................省略
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然后判断mLooper,如果mLooper不为空就把mLooper所在线程的消息队列赋值给mQueue。那么什么时候mLooper是空的呢?看看myLooper方法的代码。
代码块2
public static Looper myLooper() {
return sThreadLocal.get();
}
从这里看出Looper 保存在ThreadLocal里面,ThreadLocal这个类在这里解释一下,这个类有两个方法get( )
和set( )
,比如说我有一个叫nameThreadLocal的ThreadLocal对象,我在线程A里面调用了nameThreadLocal.set("ThreadA")
;在线程B里面掉用了nameThreadLocal.set("ThreadB");
那么我在线程A get到的就是nameA,在线程B get到的就是nameB。你可以认为每个线程都有一个袋子,在哪个线程set,get就是在哪个线程拿和放,ThreadLocal会自动判定你的线程,从而选用对应的袋子。好了,我们接着分析代码,如果当前线程没有looper那么这个方法会返回空,从Handler的构造函数可以看出mLooper为null时,它会抛出如下异常:
代码块3
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
意思是“不能在没有调用Looper.prepare( )
的线程里创建Handler”,(强调一下,我们一般在UI线程里创建Handler实例不会报这个异常是因为系统创建UI线程时会自动调用Looper.prepare( )
方法,但是我们自己创建的子线程必须手动调用prepare方法)那我们就看一下Looper的prepare方法:
代码块4
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调用的是下面的prepare,它会判断当前线程是否有looper,如果没有就创建一个并放到sThreadLocal里面,所以执行完了这个方法之后,Handler的构造函数就不会报异常了。下面我们看看Looper的构造函数:
代码块5
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在构造函数中它创建了一个消息对列,我们回到代码块1 可以看到mQueue = mLooper.mQueue
————–Looper把这个消息队列交给了Handler。这样调用了Lopper.prepare( )
并且创建了handler实例时候我们这个线程Handler,Looper,MessageQueue这三个对象都有了,下面我们来看看这三个对象是怎么工作的。
我们可以从handler.sendMessageAtTime (Message msg)
这个对象着手分析因为所有发送消息的方法最终都是调用这个方法,这个方法会发送一个Message到消息队列交给Handler处理。
代码块6
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);
}
这里直接看return这句,enqueueMessage方法会把send过来的方法放入消息队列,继续点进去enqueueMessage方法,最终发现它调用的是下面这个方法:
代码块7
boolean enqueueMessage(Message msg, long when) {
...........................略
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
这段没什么就是把这个消息加入消息队列队尾。最后就是看看消息是怎么取出然后怎么送到handler的了。这就要看看Looper的loop方法。
代码块8
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
.....................................略
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
.....................................略
msg.target.dispatchMessage(msg);
.....................................略
}
msg.recycleUnchecked();
}
代码比较乱,只截取了比较重要的loop方法首先得到了当前线程的Looper然后得到MessageQueue,然后使用 msg.target.dispatchMessage(msg);
这行代码分发给handler。msg.target就是要发送到的handler,但是我们平时用Message都是直接new出来的,也没有设置target啊,答案就在下面的代码,很简单。
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;
就是这一行。好了大致的流程就是这样,感觉看着还是很简单的。^_^ 下面我们来根据来自官方文档的代码总结一下。
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
//处理任务
}
};
Looper.loop();
}
}
从上面代码来看:
第一步:调用Looper.prepare方法(子线程需要手动调用,UI线程不需要),在当前线程创建了一个looper同时创建了一个MessageQueue并且保存起来。
第二步:创建了一个Handler并且重写了处理消息的方法并得到了当前线程的Looper和MessageQueue。
第三步:执行Looper.loop( ).这句代码会得到当前线程的looper和MessageQueue,并且不断地从MessageQueue取出消息送到Handler去处理。
上面做好了之后 当我们调用mHandler.sendMessageAtTime (new Message());
时
mHandler把这个消息的target设为当前mHandler然后把它加入到MessageQueue等待looper取出。
好了,当前就了解这么多,是不是感觉对handler了解了很多。由于博主刚刚开始写博客,水平有限,如果有错误请大家指出。