HandlerThread源码解析

作者: 小乌贼007 | 来源:发表于2019-11-12 20:17 被阅读0次

我对HandlerThread的理解是实现了一套消息通信机制的Thread。和它有关的两个重要概念是Looper(消息泵)和Handler(消息句柄),Looper可以理解成消息泵,当Thread运行起来后,Looper不断地从消息队列(MessageQueue)中取出消息并进行解释,若消息队列中没有消息可解释的消息(无消息或者有消息,但是消息还没到可解释的时间),则线程阻塞,有可解释的消息时(新的即时消息或者定时消息到了解释时间),线程被唤醒,继续解释消息。Handler用来操作线程的Looper,往Looper中添加消息,同时也负责消息的解释。所以两个HandlerThread可以通过Handler来完成线程通信。

因为Handler需要操作Looper,所以Handler需要持有Looper,Handler持有哪个线程的Looper,Handler就可以往哪个线程上发送消息和解释消息。HandlerThread消息的来源可能有多处,所以Looper可以被多个Handler持有,当需要往HandlerThread发送消息时,只需要用该HandlerThread的Looper实例化一个Handler,用该Handler就可以往HandlerThread发送消息。

每个线程都有自己独立的Looper(消息泵),所以Looper看起来像Thread的成员变量,但是Thread中没有一个叫Looper的成员变量,ThreadLocal<Looper>帮我们实现了这个需求,即:ThreadLocal<Looper>往Thread中添加了一个逻辑上的成员变量Looper。(也可以理解成每个Thread有自己独立的Looper副本),但我更喜欢逻辑成员变量这种理解方式。

一种新的方式去理解ThreadLocal及源码解析

HandlerThread 源码分析

HandlerThread类的代码相对简单,下面来看下比较重要的几个方法:

public class HandlerThread extends Thread {

    @Override
    public void run() {
        mTid = Process.myTid();
        //准备自己的消息泵
        Looper.prepare();
        synchronized (this) {
            //获取自己的消息泵
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        //Looper准备好时的回调
        onLooperPrepared();
        //消息泵启动,不断接收消息,解释消息,直到消息泵退出,该方法会阻塞线程(等待消息到来)。
        Looper.loop();
        //走到这里说明消息泵结束,线程即将终止。
        mTid = -1;
    }

    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

    /** 获取线程的消息泵,
    * 有了Looper,就可以new一个Handler来操作Looper,往该线程发送消息并解释消息。请区别getThreadHandler方法。
    */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        //返回looper
        return mLooper;
    }

    //停止消息泵,线程终止,消息泵中未执行的消息会得不到执行。
    //方法执行后往消息泵中添加消息会失败。
    //详细请看MessageQueue.quit
    public boolean quit() {
        Looper looper = getLooper();
        if (looper != null) {
            //消息泵退出
            looper.quit();
            return true;
        }
        return false;
    }

    //消息泵中的消息执行完成后,退出消息泵,线程终止。
    //队列中now之后的消息得不到执行,now以前的消息可以得到执行
    //方法执行后往消息泵中添加消息会失败。
    //详细请看MessageQueue.quitSafely
    public boolean quitSafely() {
        Looper looper = getLooper();
        if (looper != null) {
             //消息泵安全退出
            looper.quitSafely();
            return true;
        }
        return false;
    }

    /**获取该线程的Handler,因为这个Handler由HandlerTherad生成,
    *所以用该Handler进行消息通信时,发送方需要注意HandlerTherad是否认识这个消息,
    *该handler用来发送HandlerTherad本身定义好的那些消息。
    *一般会重写这个方法,返回一个能够处理一些了特定消息的Handler。
    */
//    public Handler getThreadHandler() {
//        if (mHandler == null) {
//            mHandler = new Handler(getLooper()){
//                @Override
//                public void handleMessage(@NonNull Message msg) {
//                    switch (msg.what) {
//                           case 1:
//                               //do some thing;
//                               break;
//                           case 2:
//                               //do some thing;
//                               break;
//                       }
//                }
//            };
//        }
//        return mHandler;
//    }

    public Handler getThreadHandler() {
        if (mHandler == null) {
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }
}

Looper实现原理

首先来看Looper 的初始化及获取,prepare方法利用ThreadLocal,在Thread类中添加了一个逻辑成员变量,并赋值,对ThreadlLocal不了解的可以看我另一篇文章。
一种新的方式去理解ThreadLocal及源码解析

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    //初始化当前线程的Looper,looper可结束。
    public static void prepare() {
        prepare(true);
    }
    //quitAllowed 表示十分运行线程终止,主线程的Looper不允许终止。
    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            //Looper只能初始化一次。
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

    //looper构造方法
    private Looper(boolean quitAllowed) {
        //初始化消息队列
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

   //获取当前线程的looper
   public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

    //初始化MainLooper
    public static void prepareMainLooper() {
        //初始化main looper,main looper不可终止。
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

    //获取MainLooper,用该Looper实例化一个handler可以往主线程发送消息。
    public static Looper getMainLooper() {
        synchronized (Looper.class) {
            return sMainLooper;
        }
    }

下面来看Looper工作及结束时用到的方法:

    final MessageQueue mQueue;//消息队列 待追查0
    //启动当前线程的消息泵,主要代码节选。
    public static void loop() {
        //获取当前线程的Looper
        final Looper me = myLooper();
        if (me == null) {
            //如果没有Looper则抛异常,提示先初始化当前线程的Looper
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //获取Looper中的消息队列。
        final MessageQueue queue = me.mQueue;
        //死循环
        for (;;) {
            //若queue中无消息,则线程阻塞
            Message msg = queue.next(); // might block
            if (msg == null) {
                //走到这里说明调用了quit或quitSafely方法,队列正在退出,线程即将终止。
                return;
            }
            try {
                //解释消息 待追查1 
                msg.target.dispatchMessage(msg);
            } 
            //将该消息放入消息池 待追查2
            msg.recycleUnchecked();
        }
    }

    //队列退出
    public void quit() {
        mQueue.quit(false);
    }

    //队列安全退出
    public void quitSafely() {
        mQueue.quit(true);
    }

追查下待追查1,msg.target是个Handler,看一下Handler的dispatchMessage方法。

    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            //若msg有callback,则执行callback,不需要handler再对msg进行解释。这种消息称为Callback消息。
            handleCallback(msg);
        } else {
             //走到这里的消息称为what消息,handler需要根据不同的what来做不同的事情。消息的种类在讲解Message时介绍。
            //若有mCallback成员变量,则msg由mCallback解释。
            //mCallback返回false,则继续由handler的handleMessage方法解释。
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //handler的handleMessage方法解释msg。
            handleMessage(msg);
            //这样写的原因是解释消息的方式可以有两种,
            //一种是继承Handler,然后重写handleMessage方法,
            //一种是构造Handler时传入Callback,由Callback对msg完成解释。
        }
    }

    private static void handleCallback(Message message) {
        message.callback.run();
    }
  
    //继承方式解释msg
    public void handleMessage(@NonNull Message msg) {
    }

     /**
     * Callback interface you can use when instantiating a Handler to avoid
     * having to implement your own subclass of Handler.
     */
    public interface Callback {
        /**
         * @param msg A {@link android.os.Message Message} object
         * @return True if no further handling is desired
         */
        boolean handleMessage(@NonNull Message msg);
    }

   //构造方法里传入的Callback解释msg
   final Callback mCallback;
    

所以Looper类相对简单,主要是用来初始化当前线程Looper,启动当前线程Looper,和退出当前线程Looper,主要用到的知识点是ThreadLocal,可以发现Looper类中的好多方法都是static的,想一想为什么。

下面来看消息队列中的元素Message和与Looper交互的Handler。

Message

public final class Message implements Parcelable {
    //定义了消息,因为消息由Handler解释,所以每个Handler有自己的消息空间,不用担心消息冲突。
    public int what;

    //消息参数1
    public int arg1;

    //消息参数2
    public int arg2;

    //消息参数obj,目前没发现它的用处
    public Object obj;

    //消息处理完后可以给Messenger回复消息,可实现双向通信。
    public Messenger replyTo;
  
    //消息的执行时间,milliseconds since boot, not counting time spent in deep sleep.
    public long when;

    //区别于arg1 arg2 用于传输更多数据。
    Bundle data;
    public void setData(Bundle data) {
        this.data = data;
    }
    //解释该消息的handler
    Handler target;

    //消息的callback,有callback的消息不需要what,
    //对此类消息的解释只是执行该callback,追查待追查1时可以看到。
    Runnable callback;

    //下一个消息,消息回收和消息队列会用到。
    Message next;
    //消息池的同步锁
    public static final Object sPoolSync = new Object();
    //消息池的头部节点
    private static Message sPool;
    //消息池的大小
    private static int sPoolSize = 0;
    //消息池最大大小
    private static final int MAX_POOL_SIZE = 50;

    //表示msg在用
    static final int FLAG_IN_USE = 1 << 0;
    //表示msg是异步msg
    static final int FLAG_ASYNCHRONOUS = 1 << 1;
  • 一个Message必须有target,该消息由target指定的handler来解释(同时handler指定了消息解释发生在哪个线程),若Message无target则该消息是个屏障消息(待追查0)。

  • 通过对Handler的dispatchMessage方法的分析,若Message有callback(Runnable类型),则不需要指定what及其他参数(arg1,arg2,bundle,obj),应用场景是指定在某个线程上运行一段代码,如 mainHandler.post(Runnable r) ,这类消息我称之为callback类Message。

  • 若Message无callback,则需要指定what,其他参数是可选项。该类消息由Handler的mCallback成员或着handleMessage方法解释,这类消息我称之为what类Message,what类Message发送方和接收方都需要明白what的含义。

读到这里应该可以领悟HandlerThread中的getThreadHandler方法和getLooper方法使用场景的区别。由getLooper方法返回的Looper构造出的Handler一般只用来发射callback类Message。

Message复用

由于Message在HandlerThread中使用频繁,所以一般不要自己去new Message,而是从Message池获取,这样可以省去频繁构造释放Message,从而提高性能。Message的获取一般使用Message的obtain方法,obtain有很多重载方法,这里只讲两个。

   //用来获取what类Message
   public static Message obtain(Handler h, int what) {
        Message m = obtain();
        m.target = h;
        m.what = what;
        return m;
    }

    //用来获取callback类Message
    public static Message obtain(Handler h, Runnable callback) {
        Message m = obtain();
        m.target = h;
        m.callback = callback;

        return m;
    }
    //Message池中获取Message对象。
    public static Message obtain() {
        //Message池同步锁
        synchronized (sPoolSync) {
            if (sPool != null) {
                //若存在Message池,返回sPool指向的Message对象,
                //sPool指向下一个Message
                Message m = sPool;
                sPool = m.next;
                //清理msg
                m.next = null;
                m.flags = 0; // clear in-use flag 
                //Message池长度-1
                sPoolSize--;
                return m;
            }
        }
       //若不存在Message池,则构造一个Message返回,
       //当该Message被会回收时,若回收池未满则该Message会被回收进回收池,再需要Message时,可从Message池中取Message,
        return new Message();
    }

    //将Message回收,追查待追查2
    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 = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            //若回收池未满,则将Message放入Message池。
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

Handler

Handler主要有三个功能,

  • 获取消息
  • 往Looper的消息队列中添加消息或者删除消息
  • 解释消息(追查待追查1已分析)
    Handler的构造方法主要有三个参数,Callback、Looper、和async。Callback用来解析Message(请回看追查待追查1)。Looper用来指定Handler操作的Looper,进而指定解析消息时发生在哪个线程,若构造Handler时未指定Looper,则直接获取当前线程的Looper。async用来标识发送的消息是否是异步消息,待追查0。
   public Handler(@Nullable Callback callback, boolean async) {
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

   public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

获取消息、发送消息及删除消息:

   //获取消息
   public final Message obtainMessage(int what, int arg1, int arg2) {
        //从消息池中获取消息
        return Message.obtain(this, what, arg1, arg2);
    }

   //发送消息
   public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }

   //发送延时消息
   public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

    //runnable生成callback类Message
    private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

  //发送消息
  public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        //即时消息 when = SystemClock.uptimeMillis()
        //定时消息 when = SystemClock.uptimeMillis() + delayMillis
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
   
    //发送定时消息
   public boolean sendMessageAtTime(@NonNull 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);
    }

    //将消息放入消息队列
   private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
         //如果是异步消息 待追查0,设置异步标志
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

    //删除消息
    public final void removeMessages(int what) {
        mQueue.removeMessages(this, what, null);
    }

    //在队列头部插入一个消息,该消息的when=0
    public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
        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, 0);
    }

还有一个方法可以打印出队列里的消息,可以用于调试。

    public final void dump(@NonNull Printer pw, @NonNull String prefix) {
        pw.println(prefix + this + " @ " + SystemClock.uptimeMillis());
        if (mLooper == null) {
            pw.println(prefix + "looper uninitialized");
        } else {
            mLooper.dump(pw, prefix + "  ");
        }
    }

当目前为止还有追查0没有追查,涉及到MessageQueue,屏障消息(target=null的Message)和异步消息,这些内容在介绍MessageQueue时介绍。

MessageQueue

消息队列是一个比较成熟的概念,模型应该不难理解,MessageQueue在数据结构上使用的是有序单向链表,每次往队列插入消息时,会根据消息的时间将消息插入合适的位置,链表头的消息最先得到解释(一般是即时消息,或者定时消息到了解释时间,Message.when值最小),链表尾的消息最后得到解释(一般是定时消息,消息的解释时间未到,Message.when值最大)。每次取消息时,总是取链表头部的消息,若链表无可执行的消息(链表无消息,或者链表里的消息还没到该执行的时间)线程进入阻塞,直到有新的消息需要解释(队列里有消息到了执行时间,或者有新的即时消息被添加进来),线程唤起(唤起方式有两种,新的即时消息被添加进来是手动唤醒,定时消息到了执行时间是定时唤醒),线程唤起后将继续解释消息。当调用quit时,会将队列里的所有消息清除,消息队列退出,线程退出。当调用quitSafely时,会将队列里的未来消息(when>now)清除,执行完剩下的消息后,消息队列退出,线程退出。MessageQueue的工作原理并不复杂,万事就怕但是,但事情也并没有那么简单。

MessageQueue大部分时间的工作方式如上述所说,但是主线程有些消息有插队的需求,比如主线程每隔16ms需要重新渲染ui,当渲染ui的消息来到时,若不能插队,等之前的消息解释完之后再去渲染ui,难免会造成ui的卡顿。所以MessageQueue有两套机制来实现消息插队。

  • 第一种是设置when=0的消息,然后将这个消息放在队列头部,(一般的消息when=now>0),Handler.sendMessageAtFrontOfQueue方法可以发送该类型消息,when=0的消息比即时消息先得到执行。

  • 第二种是异步消息,异步消息的优先级大于同步消息,MessageQueue在取消息时若发现头部是个屏障消息(target=null)的消息,则只从队列中取异步消息进行解释,若无可解释的异步消息(无异步消息,或异步消息未到解释时间),则线程阻塞。只有将队列的消息屏障移除之后MessageQueue才会去取同步消息进行解释。异步消息相当于贵族,同步消息相当于平民,消息屏障相当于信号灯,当信号灯亮时,只允许贵族通过,平民等待。信号灯灭后,平民继续通过。

情况就是这么个情况,下面来看取消息操作的next方法

    Message next() {

        final long ptr = mPtr;
        //pter=0表示队列退出了,返回null,回看下Looper.loop。
        if (ptr == 0) {
            return null;
        }
        //pendingIdleHandlerCount 队列空闲回调数量
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        //阻塞时长,
        int nextPollTimeoutMillis = 0;
        for (;;) {
            //不知道是干嘛的(未来再看)
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //阻塞nextPollTimeoutMillis毫秒后唤醒我,
            //nextPollTimeoutMillis=0表示不阻塞,
            //nextPollTimeoutMillis=-1表示永久阻塞,队列中无消息,等待手动唤醒(新的即时消息到来)
            //nextPollTimeoutMillis>0表示阻塞nextPollTimeoutMillis毫秒后自动唤醒该线程,发生在队列头部的消息未到解释时间,
            //当队列头部的消息到解释时间了再叫醒我,我再来干活。
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                // Try to retrieve the next message.  Return if found.
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                //取头部消息进行解释
                Message msg = mMessages;
                //若头部消息是个屏障消息,则取队列中的异步消息来解释
                if (msg != null && msg.target == null) {
                    // Stalled by a barrier.  Find the next asynchronous message in the queue.
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                //若消息不为空(该消息可能是同步消息,也可能是异步消息)
                if (msg != null) {
                    if (now < msg.when) {
                        //该消息未到解释时间,则计算阻塞时长,线程将阻塞
                        //nextPollTimeoutMillis毫秒后被唤醒(定时唤醒)
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //消息到了解释时间
                        mBlocked = false;
                        if (prevMsg != null) {
                            //消息在队列中间,说明消息是个异步消息,将该消息从队列中摘掉
                            prevMsg.next = msg.next;
                        } else {
                            //消息在队列投,说明消息是个同步消息,将消息从队列中摘掉
                            mMessages = msg.next;
                        }
                        //断开消息和队列的联系
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        //标识消息在使用
                        msg.markInUse();
                        //返回该消息
                        return msg;
                    }
                } else {
                    //若没有消息,则设置阻塞时长为永久,只有手动唤醒线程(新即时消息到来)
                    nextPollTimeoutMillis = -1;
                }

                //队列退出,清理工作,mPtr会置空,返回空
                if (mQuitting) {
                    dispose();
                    return null;
                }

                //计算idleHandlerListener的数量
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                //若没有idleHandler,则不执行idleHandler的回调,回到for头部
                //此时nextPollTimeoutMillis!=0,
                //回到for循环头部执行nativePollOnce(ptr, nextPollTimeoutMillis)
                //线程进入阻塞状态,等待唤醒
                if (pendingIdleHandlerCount <= 0) {
                    mBlocked = true;
                    continue;
                }
                //将mIdleHandlers中的listener拷贝到新的数组。拷贝操作是为了尽早退出临界区
                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            //回调IdleHandler
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }
            //注意这里退出了临界区。
            //pendingIdleHandlerCount赋值0,在一个for循环内(一次next方法中),IdleHandler只会执行一次。
            pendingIdleHandlerCount = 0;
            //因为在执行IdleHandler的过程中(未在临界区)可能有新的消息被放入队列,
            //所以不阻塞,回到for循环看看有没有消息。
            nextPollTimeoutMillis = 0;
        }
    }
  
    //队列退出,清理工作
    private void dispose() {
        if (mPtr != 0) {
            nativeDestroy(mPtr);
            mPtr = 0;
        }
    }

    public static interface IdleHandler {
        /**
         * Called when the message queue has run out of messages and will now
         * wait for more.  Return true to keep your idle handler active, false
         * to have it removed.  This may be called if there are still messages
         * pending in the queue, but they are all scheduled to be dispatched
         * after the current time.
         */
        boolean queueIdle();
    }

    //存放IdleHandler,当队列为空时会回调queueIdle方法。
    private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    private IdleHandler[] mPendingIdleHandlers;

下面来看添加消息的enqueueMessage方法,屏障消息的添加不走这里(postSyncBarrier用来添加屏障消息)。

     boolean enqueueMessage(Message msg, long when) {
        //消息合法性判断
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        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();
            //设置消息的解释时间
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //将消息添加到队列头
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                //是否需要唤醒取决于是否已经阻塞,这里适用于普通消息
                needWake = mBlocked;
            } else {
                //将消息添加到队列中部(头以后)
                //走到这里对于同步消息来说,不需要唤醒线程,因为头部消息已经设置了定时唤醒。
                //对于异步消息来说若头部是屏障消息且该消息的when小于消息队列中所有异步消息的when,则需要唤醒,
                //唤醒后线程进入next方法的for循环,会重新计算休眠时长或直接解释该消息。
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                //将消息插入到合适的位置
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        //找到消息添加的位置p
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        //若队列中存在when更小的异步消息,则不需要唤起线程
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

           //若需要唤醒线程,则唤醒线程(这里是手动唤醒)。
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

来看消息屏障的添加及删除:

   private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
       //消息屏障的添加不需要唤醒线程
        synchronized (this) {
            //token用来标识消息屏障,需要用该token来删除消息屏障
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            //消息屏障也有时间
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            //找到合适的位置
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                //插入到头部
                msg.next = p;
                prev.next = msg;
            } else {
                //插入到中部
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

    public void removeSyncBarrier(int token) {
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            //查找屏障的位置
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            //未找到屏障,不科学,抛异常
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            //若屏障在队列中部,则不需要唤醒线程
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                //若屏障在头部   
                //新队列为空或者新队列头部不是屏障消息则唤醒线程
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            //回收消息
            p.recycleUnchecked();

            //队列未退出且需要唤醒线程则唤醒线程。
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

下面来看两个退出方法:

    void quit(boolean safe) {
        //若不允许退出,则抛异常
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
        //
        synchronized (this) {
            //若已经退出,则返回
            if (mQuitting) {
                return;
            }
            //设置退出标志位
            mQuitting = true;
            if (safe) {
                //安全退出
                //清除所有未来消息
                removeAllFutureMessagesLocked();
            } else {
                //不安全退出
               //清除所有消息
                removeAllMessagesLocked();
            }

            //唤醒线程做清理工作
            nativeWake(mPtr);
        }
    }

    //清除所有未来消息
    private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            if (p.when > now) {
                //都是未来消息,则全部清除
                removeAllMessagesLocked();
            } else {
                Message n;
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                //n是未来消息的起点,
                p.next = null;
                //回收未来消息
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }
    
    //清除所有消息
    private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            //回收消息
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }

主要方法讲完了,看看其它不是很重要的方法

  //判断队列中是否有某个handler的消息
  boolean hasMessages(Handler h) {
        if (h == null) {
            return false;
        }

        synchronized (this) {
            Message p = mMessages;
            while (p != null) {
                if (p.target == h) {
                    return true;
                }
                p = p.next;
            }
            return false;
        }
    }

  //删除消息
  void removeMessages(Handler h, Runnable r, Object object) {
        if (h == null || r == null) {
            return;
        }

        synchronized (this) {
            Message p = mMessages;

            // Remove all messages at front.
            //在头部删除
            while (p != null && p.target == h && p.callback == r
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }

            //在中部删除
            // Remove all messages after front.
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.callback == r
                        && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

说到这里,突然想起来个问题,既然UI绘制走的是高优先级的异步消息,为啥卡顿还会发生呢?
卡顿产生的原因:16ms内未完成ui的绘制
两个原因:

  • ui太复杂,16ms确实没绘制完成,一般不会有这么复杂的ui吧?
  • 16ms的区间内,留给ui绘制的时间太少。不是走的特殊通道吗,为啥还会太少?虽然走的特殊通道,但绘制ui的消息最快也要等到当前消息解释完成后才能得到解释。如果当前消息的解释花费了很长时间,那么势必会挤占16ms的绘制区间。所以如果一个操作需要花费几毫秒以上的话,将它放到work thread执行吧。

突然又想到一个问题,主线程会阻塞吗?主线程如果阻塞了怎么办?
我的猜想,主线程也会阻塞,android是事件驱动的,当有事件来临时(如点击事件),应该会唤起主线程(其它线程用主线程的handler发送消息)。点击事件应该由系统传过来,通过binder到达binder线程,binder线程唤起主线程?只是猜想,后面再跟进这个问题。

nativePollOnce是个native方法,搜了一下底层是用epoll实现的,后面还需要再跟进一下这个知识点。

今天就先到这儿了。

关于消息插队的应用场景请看这篇文章,写的很好:
android屏幕刷新机制

相关文章

  • 3.3异步消息处理机制-HandlerThread

    HandlerThread handlerThread是什么 handlerThread源码解析 1.handle...

  • 10 异步3-HandlerThread

    1)handlerThread是什么2)handlerThread源码解析 1、handlerThread产生背景...

  • HandlerThread 源码解析

    在日常的开发中经常需要使用到Handler这个类,我们都知道Handler 必须要和 Looper 中结合使用,尤...

  • HandlerThread源码解析

    HandlerThread源码 它是一个Thread的子类,源码很简单,只有100多行代码1、 run() 可以看...

  • HandlerThread 源码解析

    HandlerThread 是什么? 系统对 HandlerThread 的解释是这么一段话: 从上面这段话可以看...

  • HandlerThread源码解析

    上一篇文章对HandlerThread的概念及用法做了一个基本的介绍,并且使用HandlerThread改写了相册...

  • HandlerThread源码解析

    1、HandlerThread 是什么? 它继承至 Thread,具备线程的特性。它是一个带有 Looper 的线...

  • HandlerThread源码解析

    概述 HandlerThread是Thread的子类,由HandlerThread创建的子线程可以直接创建Hand...

  • HandlerThread源码解析

    我对HandlerThread的理解是实现了一套消息通信机制的Thread。和它有关的两个重要概念是Looper(...

  • HandlerThread源码解析

    其实从HandlerThread这个名字大家应该也能猜得出来,这是Handler和线程相关的类,如果你理解了Han...

网友评论

    本文标题:HandlerThread源码解析

    本文链接:https://www.haomeiwen.com/subject/rhetictx.html