Handler--安卓之功臣

435 阅读8分钟

前言

大家好,我是猪猪侠,又和大家见面了。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是消息的载体 今天就先将到这里,猪猪有什么讲错的或者有什么好的建议,希望可以给到猪猪,谢谢大家。