Handler源码分析

10,051 阅读5分钟

Handler是Android开发中必不可少的组建,大部分情况下我们用来做跨线程通讯,但是Handler的设计并不只是为了做线程通讯,它是android的事件处理核心机制,作为源码阅读的第一步可以很好的帮助我们深入理解android的一些设计

核心类:1:Handler 2:Looper 3:MessageQueue 4:Message

一句话概括handler原理: Looper源源不断的将MessageQueue中的Message提取出来交给用户定义的Handler调用。

未了方便理解,我决定从简单的组建开始分析。

1、Looper

Looper类似于泵,远远不断将水池(MessageQueue)中的水(Message)抽取出来交给用户(Handler)。

这是looper的官方注释:

Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call{@link #prepare} in the thread that is to run the loop, and then {@link #loop} to have it process messages until the loop is stopped.

可以看到线程默认是没有looper的,需要调用prepare()方法然后调用loop()方法启动循环。

筛选出几个比较重要的属性:

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;
final MessageQueue mQueue;
final Thread mThread;

可以看到,使用ThreadLocal保证了每个线程有一份looper,而主线程的Looper单独定义了出来。

关于Prepare的几个方法:

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));
}
  
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

prepare()方法比较简单,就是初始化一个MessageQueue以及thread存储在本地。

重点在loop()方法:

    public static void loop() {
       .....
        for (;;) {
            if (!loopOnce(me, ident, thresholdOverride)) {
                return;
            }
        }
    }
    
    private static boolean loopOnce(final Looper me,
            final long ident, final int thresholdOverride) {
        Message msg = me.mQueue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return false;
        }
      ........
        msg.target.dispatchMessage(msg);
      .........
        return true;
    }    

可以看到,loopOnce的作用就是从queue中得到下一条的msg,并调用msg.target.dispatchMessage(msg),而这个target其实就是handler,也就是说,message本身带着handler,looper将msg交给他自己带着点hanler去回调dispatchMessage交给用户处理,而由于handler定义的线程和发送消息的不在一个线程,于是就实现了在B线程发送消息交给了A线程中定义的handler,实现了跨线程的通信(后面分析Handler会具体讲到发送过程)。

2、Message

接下来分析Message类,一些比较关键的属性

Handler target;
Message next;
private static Message sPool;
static final int FLAG_ASYNCHRONOUS = 1 << 1

1、target: target就是每条msg对应的handler,handler会在sendMessage的时候将自身和线程信息一起带给msg。

2、next:没错,这里其实就是队列数据真正存放的地方,messageQueue的数据之间用链表实现,在message中用next指向下一个对象,而messageQueue内部会其实主要负责索引和指针的变化。

3、sPool:又一个message,这个属性根据名字就能知道,这是一个消息池,类似线程池,减少创建message的开支,obtain会直接复用池里的对象:

    /**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                //永远将sPool指向下一个message
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

4、FLAG_ASYNCHRONOUS:Message是否同步。这里引出一个非常重要的概念:同步屏障以及message的类型(同步、异步、同步屏障)。

同步屏障

通过之前的概念我们知道,message是存放在MessageQueue中的,而Message只能从头添加消息,而添加消息是按照消息的执行顺序进行排序的(这里我会在MessageQueue分析源码)。而如果同一时间范围内,有消息需要立即执行(比如view的绘制),该怎么办,如果按照正常方法需要等待前面的消息全部执行完成,这样便会造成阻塞,于是我们便需要一个绿色通道,所有处于绿色通道内的消息全部有限执行,这便是同步屏障。

同步屏障的原理:在messageQueue中插入一个target==null的messageA,所有同步message都在这个messageA之前,异步message都在messageA之后,messageQueue在调用next()时候,会优先调用messageA之后的异步消息(异步消息执行的很快不会阻塞),之后才会调用messageA之前的同步message。(源码在messageQueue中)

3、MessageQueue

MessageQueue是消息队列,从上面的文章我也提到了,真正的链表是直接存放在message中的,messageQueue主要负责操作,主要分析几个流程中比较重要的属性和方法

Message mMessages;//队列头
    Message next() {
        //如果当前queue已经退出或者被释放,则返回null
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }
        //初始化当前idleHandler数量
        //step1
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        
        //下次调用的时间
        int nextPollTimeoutMillis = 0;
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
            //这是一个native方法,主要作用是等待直到下一条message可用为止(即到了调用时间)
            //利用了linux中的epoll_wait等待
            //这篇文章会分析native侧的源码
            //https://www.cnblogs.com/jiy-for-you/archive/2019/10/20/11707356.html
            nativePollOnce(ptr, nextPollTimeoutMillis);
            
            //找到下一条可以执行的msg
            synchronized (this) {
                //当前时间
                final long now = SystemClock.uptimeMillis();
                //遍历中的先驱节点prev
                Message prevMsg = null;
                //当前遍历节点
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
                  //target==null表示当前节点是一个同步屏障
                  //一旦当前节点是同步屏障,则遍历规则就是跳到最近一个异步消息
                    do {
                      //如果当前节点是一个同步消息,则msg=msg.next,遍历到下一个节点
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                //遍历到了异步消息节点
                if (msg != null) {
                    if (now < msg.when) {
                         //当前msg的调用时间when还未到达(now<when)更新等待时间(when-now),
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        //已经到达等待时间,可以执行消息
                        //当前队列没有阻塞
                        mBlocked = false;
                        if (prevMsg != null) {
                            //常规链表操作,将pre和next连接
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        //找到的msg做一些数据上的处理,删除next
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                        //标记use
                        msg.markInUse();
                        //返回msg
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }
                
                /*************接下来的逻辑是关于IdelHandler的****************/
                //IdleHandler是一个内部接口,主要是用来在messageQueue空闲时调用的
                //当queue中没有消息或者queue中消息还未到执行时间就会进入空闲,也就是走到这一步代码。
                //IdleHandler只有一个方法boolean queueIdle(),返回true表示会重复调用
                //也就是下次空闲时还会回调
                //step2
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                //step3
                if (pendingIdleHandlerCount <= 0) {
                    //当前没有idleHandler处理,真正进入了阻塞状态
                    mBlocked = true;
                    //进入下一次循环
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                //将所有add的idleHandler传入临时数组中
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            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 {
                  //回调idleHandler
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf(TAG, "IdleHandler threw exception", t);
                }
                  
                if (!keep) {
                  //如果释放了,则从list中移除该handler
                    synchronized (this) {
                        mIdleHandlers.remove(idler);  
                    }
                }
            }

            //这里将count置为0,因为当idleHandler返回true时,会继续存在,此时如果继续循环,就会进入
            //死循环不停调用,置为0可以结束本次的idleHandler调用
            //step=4
            pendingIdleHandlerCount = 0;

            //第二次循环,由于pendingIdleHandlerCount==0,所以在step2处就会continus
            //于是nextPollTimeoutMillis就不会再次被赋值=0,此时nextPollTimeoutMillis=-1或者是
            //需要等待的时间 when-now
            //所以就会在native侧真正的进入等待,知道下次的唤醒
            nextPollTimeoutMillis = 0;
        }
    }
    
    //内部定义的接口,空闲时handler
    //主要在messageQueue空闲时会调用IdleHandler
    //我们可以放一些需要延迟但又不确定具体延迟多久的任务
    //注意不能过分依赖这个东西,因为调用的不确定性,无法确定queue具体空闲时间
    //activityThread中使用IdleHandler来进行GC(间隔5s以上)
    public static interface IdleHandler {
      //返回true表示这个handler会继续留存在queue内部,下次空闲时候还会调用
      //false则会在完成调用后从list中remove
        boolean queueIdle();
    }

    public void addIdleHandler(@NonNull IdleHandler handler) {
        if (handler == null) {
            throw new NullPointerException("Can't add a null IdleHandler");
        }
        synchronized (this) {
            mIdleHandlers.add(handler);
        }
    }

    public void removeIdleHandler(@NonNull IdleHandler handler) {
        synchronized (this) {
            mIdleHandlers.remove(handler);
        }
    }    


在Message源码和next()方法里都提到了关于同步屏障,同步屏障的作用就是在队列中插入一个target==null的特殊message,在next()方法中,如果queue下一个msg是一个同步屏障,则遍历queue找到第一个异步msg,直到所有异步msg全部出队列,实现了异步消息的优先出队列。同步屏障的add和remove如下:

    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }
    //插入同步屏障
    private int postSyncBarrier(long when) {
        synchronized (this) {
            final int token = mNextBarrierToken++;
            //消息池里得到一个新的消息,target==null
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            //根据消息执行的时间when找到同步屏障节点插入的位置
            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存在)              
                prev.next = p.next;
                needWake = false;
            } else {
            //如果同步屏障就是头节点,则根据是否存在下一个节点next来判断是否需要唤醒queue
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            //回收msg资源
            p.recycleUnchecked();

            //如果需要唤醒队列,则调用native侧的唤醒方法
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

说完next和同步屏障,MessageQueue中剩下的比较重要的方法就是消息的如队列Enqueue了:

    boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        //在这里加锁,就实现了每个queue的线程安全
        //由于每个thread拥有一个lopper,每个lopper拥有一个enqueue,每个queue的enqueue又是线程安全的
        //于是就实现了所有子线程向主线程发送消息的线程安全
        synchronized (this) {
            //当前消息正在被使用
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
            //当前队列正在关闭
            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的when小于队列头msg的when,需要优先执行
                //根据当前是否阻塞决定是否需要唤醒
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                //如果当前头节点是一个同步屏障而且当前插入msg是一个异步消息,则表示需要立即执行
                //于是需要唤醒队列
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                //根据when找到对应的插入位置
                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;
    }

4、Handler

先看Handler的构造方法:

    public Handler(@Nullable Callback callback, boolean async) {
      //Handler一般要求使用内部静态类的形式定义,防止内存泄漏
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
        //handler构造的时候会获取到当前线程的looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //获取到当前looper的messageQueue,每个线程有唯一的messageQueue
        mQueue = mLooper.mQueue;
        //自定义事件处理回调callback
        mCallback = callback;
        //当前handler内的消息是否全部定义为异步消息
        mAsynchronous = async;
    }

Handler是处理Message的地方,在Looper中会最终回调Message.target.dispatchMessage(Message msg),这个方法定义是这样的:

    public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
          //如果msg自身带有callback,就交给msg自己的回调处理,否则交给handler的callback处理
            handleCallback(msg);
        } else {
            if (mCallback != null) {
              //优先交给handler自己的callback
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //最后交给回调
            handleMessage(msg);
        }
    }

Handler中还有一个常用的方法就是sendMessage,它最终调用的是sendMessageAtTime:

    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);
    }

可以看到,sendMessage最终调用的是MessageQueue的enqueueMessage,而queueMessage又是线程安全的,所以handler处理消息也线程安全的。

Handler处理消息的完整流程

当handler在ThreadA中初始化的时候便会拿到当前线程中ThreadLocal的looper以及messageQueue,当子线程ThreadB调用 Handler.sendMessage时,最终会调用ThreadA的messageQueue.enqueue,而这个方法是synchronized(this)加锁的,使得每个msg在加入队列的时候线程安全且根据调用事件when排序(并且根据同步屏障优先调用异步msg)。ThreadA中的looper会循环调用MessageQueue的next()方法(该方法会根据when找到下一个需要执行的msg,如果时间未到,则会进入阻塞睡眠,由linux的epoll机制唤醒,期间会适当调用idleHandler)得到下一个需要处理的msg,回调该msg的target的dispatch方法,根据需要回调给用户。

Handler可以实现跨线程通讯的原理

每个handler会在自身初始化的线程中得到对应的messageQueue,所有的消息都发送给该messageQueue,而发送方的线程没有限制,于是现实了跨越线程的通信。


结语

java侧的Handler源码分析到这大体结束了,handler在c/c++层仍有不少内容(在native层还有一个messageQueue和looper!),它的阻塞和唤醒利用了linux的epoll机制,感兴趣的朋友可以查一查native层的实现,感谢大家观看,求赞~~~。