前言
大家好,我是猪猪侠,又和大家见面了。Handler--大家并不陌生,都应该使用过。它可以说是我们安卓中不可或缺的一部分。安卓是典型的多线程程序,而安卓在设计之初又规定不能在子线程更新UI,所以更新UI必须切换到主(UI)线程。那在子线程要如何切换到主线程呢?没错,就是Handler。现在很多框架从外表看,好像我们任务是在子线程做的,然后更新UI时不用切换UI。像RxJava,Retrofit,AsyncTask等等,其实它们内部都是对Handler进行了封装,从而让我们误以为更新UI不需要在主线程。那么我们这期就来看看Handler的源码和一些细节,说的不到位,还请大家见谅,下面来看一下价值500万的图。
Handler流程图
上面是根据自己对Handler的理解画的一张大概的流程图,大家先看看有个印象,下面的内容会一一细说。Handler主要涉及到的类有Handler、MessageQueue、Looper、Message、ThreadLocal等。
Handler源码解析
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//调用MessageQueue的enqueueMessage方法
return queue.enqueueMessage(msg, uptimeMillis);
}
我们一般使用Hnadler都是使用sendEmptyMessage()或者sendMessage()等这些方法,包括postMessage()等方法。在Handler内部会调用sendEmptyMessageDelayed()等一系列的方法,最后都会调用Handler的enqueueMessage(),紧接着调用MessageQueue的enqueueMessage方法
boolean enqueueMessage(Message msg, long when) {
//判断该Message携带的Handler是否为空
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
//校验该Message是否正在使用
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
synchronized (this) {
if (mQuitting) {//判断调用发送消息的线程是否还存活
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
msg.markInUse();//将Message的状态置为正在使用状态
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
//将Message放在链表的头
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
//将Message根据执行的时间顺序进行排序
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;
}
上面是MessageQueue中的enqueueMessage()方法的源码,我也加了一些注释。MesageQueue典型的一个链表结构,在方法的前面会进行一些校验,如Mesage携带的Handler是否为null,Message是否正在使用,发送Message的线程是否存活,如果条件不满足,就会抛出异常。紧接着就会对Message的执行顺序进行排序,执行时间最短的会放在连接头,然后依次按照执行顺序排放。下面是Mesage存放的大概流程图
上面的time表示执行的时间,时间执行时间是当前的毫秒值加上你发送Mesage传递的延迟时间,具体可以看下面Hnadler中的sendMessageDelayed方法
public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
下面我们来看Lopper.loop方法,由于这个方法太多,我只拿出一部分
publi static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
......
for(;;){
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
我们看到最开始,调用了myLooper()方法,如果looper为null就抛出异常,这个异常有的同学应该见过吧。我们再来看看mylooper方法
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
可以看到sThreadLocal.get()获取looper。那这个sThreadLocal是个什么东东呢?简单的说,它就是一个容器,起到了线程隔离的作用,就是保证每个线程有且只有一个Looper对象。这里因为时间关系,就不多说了,后面有机会再和各位同学一起聊聊。大家也可以参考这篇-ThreadLocal就是这么简单 .那这个Looper是在什么设置进去的呢?
public static void main(String[] args){
......
Looper.prepareMainLooper();
Looper.loop();
......
}
大家看到这个main方法是不是觉得既熟悉又亲切,它是java程序的入口,在安卓中只是一个普通的方法。这个是在ActivityThread类中的一个方法,大家不要被它的名字所迷惑了,它不是线程,只是一个普通的类。在安卓的主线程启动的时候,该方法会被调用。这就是为什么主线程不需要我们去获取looper对象和loop方法。而且子线程如果使用Handler则需要设置一个looper对象和手动调用loop方法。那这个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();
}
}
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));
}
大家看到首先判断是否已经存在Looper对象,如果存在就会抛出异常。然后创建Looper对象放到ThreadLocal容器中。关于Looper对象是怎么获取的我们就说到这里,我们在回到Looper.loop方法中,紧接着会进行一系列的判断,知道轮询到Message后,会调用msg.target.dispatchMessage(msg) 这个方法,msg.target其实就是我们在调用sendMessage或者postMesage时的Handler,Message就这个Handler作为它的一个成员变量。 那我们就去看Handler的dispatchMessage方法吧。
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
public void handleMessage(Message msg) {
}
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
首先会判断这个Message有没有callback,如果有就会执行这个callback。那这个callback是什么呢,它其实就是一个Runnable,也是Message的成员变量,我们可以通过Message进行赋值。不过它不是public,而且它的setCallback(Runnable r)这个方法也就@hide注解了。 所以要使用普通手段是行不通的了。只不过这个Message自己的callback回调我们几乎是用不到的,所以也没必要去关心它。
然后如果这个callback为null就会执行else里面的逻辑,大部分也是这么个执行流程。可以看到又有个mCallback,那这个mCallback是什么呢?它是Handler内部的一个接口,Handler有个构造方法就是可以传递这个Callback的,如果这个返回的true,那么下面的handleMessage就不会执行了,如果返回false就会执行。 而这个handleMessage就是我们平时最常使用的对Message处理的回调方法。
这里执行完之后,在Looper.loop方法中会执行msg.recycleUnchecked()方法。这里就是对Message进行回收,将它放到Message的缓存池中。不过在将这个之前,我们先来看看是如果从Message的缓存池中获取Message的。
**Handler的obtainMessage方法
public final Message obtainMessage()
{
return Message.obtain(this);
}
public final Message obtainMessage(int what)
{
return Message.obtain(this, what);
}
public final Message obtainMessage(int what, Object obj)
{
return Message.obtain(this, what, obj);
}
public final Message obtainMessage(int what, int arg1, int arg2)
{
return Message.obtain(this, what, arg1, arg2);
}
public final Message obtainMessage(int what, int arg1, int arg2, Object obj)
{
return Message.obtain(this, what, arg1, arg2, obj);
}
Message的obtain方法
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
public static Message obtain(Message orig) {
Message m = obtain();
m.what = orig.what;
m.arg1 = orig.arg1;
m.arg2 = orig.arg2;
m.obj = orig.obj;
m.replyTo = orig.replyTo;
m.sendingUid = orig.sendingUid;
if (orig.data != null) {
m.data = new Bundle(orig.data);
}
m.target = orig.target;
m.callback = orig.callback;
return m;
}
.....Message的obtain方法很多,我这里就不一一黏贴了
我们平时使用使用Handler时,一般都是通过Handler.obtain()方法从缓存池去获取Message,而不是去直接new一个Message。我们看到最终调用Message.obtain(),如果缓存池中,就直接从缓存池中拿,如果没有就会去重新创建。然后会将我们的what、ojb等一些参数赋值给当前的Message,最会返回给我们。
接着我们再来看看Message的回收
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
Message的回收就简单的很多了,就是将Message的成员变量重置为默认值或者null。到这里Handler的大概的流程以及对应的源码我们都说完了。相信很多同学都会有一个疑问,就是Looper.loop方法是死循环,如果没有Message时会阻塞,那为什么不会造成应用的卡顿或者死机呢? 我们知道安卓是基于事件工作的,任何操作都是经过Handler。不仅仅是我们平时在子线程做耗时任务,然后切换到主线程更新UI。比如我们启动一个Activity,那也是通过Handler。进行阻塞也是保证我们的主线程能够一直存活下去的一种方式。还有安卓是基于Linux,涉及到pipe/epoll机制。猪猪能力有限,只是知道有这么个东西,也无法给大家讲解Linux的pipe和epoll机制。大家可以参考这篇文章,里面有关于这个问题的一些讨论和讲解
总结
今天我们大家对Hnader的整体流程和源码进行梳理,总结一下
- Handler就是发送消息和对消息进行处理
- Looper进行消息轮询
- MessageQueue是消息的存放队列
- Message是消息的载体 今天就先将到这里,猪猪有什么讲错的或者有什么好的建议,希望可以给到猪猪,谢谢大家。