美文网首页
Handler 源码解析

Handler 源码解析

作者: 我也不知道aq | 来源:发表于2021-09-23 00:28 被阅读0次

    Handler 相关

    [TOC]

    介绍

    Android 主要是靠事件驱动的,而Handler在其中扮演重要的角色

    Handler 是Android 中的一种消息的发送、处理机制.是Android中开发中常用的机制,在这简单说明下.

    与之密切相关的有Looper,Message,MessageQueue.

    整个机制的流程:

    大致为如下

    --> 发送消息-->添加到消息队列-->消息队列取出消息--->处理消息

    整体的结构类似于生活中常见的传送带结构

    可以带着以下几点问题

    1. 什么是消息,如何处理消息的;
    2. 如何发送消息,消息如何添加到消息队列中,
    3. 消息队列中如何取出消息,按什么样的规则
    4. 一个线程有几个Looper,怎么实现的不同线程间的通信.
    5. 异步消息是什么?
    6. 主线程中的Lopper中死循环,为何不会造成ANR

    术语说明

    消息(msg) : Message
    

    Message

      即Handler 发送与处理的对象。内部存在一个target 保存发送处理此msg的handler

    节选部分关键源码

    public int what;
    @UnsupportedAppUsage
    /*package*/ Handler target;
    // sometimes we store linked lists of these things
    @UnsupportedAppUsage
    /*package*/ Message next;
    private static Message sPool;
    public long when;
    

    从源码中我们可以很明显的看出,Message是一种链式的结构,next 指向下一个Message,

    其中target 是Handler 类型,也正是我们处理消息的对象。

    一种很典型的命令模式

    Handler

      首先看下Handler,发送处理消息都是通过handler进行的,所以可以看下handler的构造方法,

    一般情况最好自己传入声明的Looper,Handler 与 传入的Looper绑定

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

    Looper

    Looper 负责管理MessageQueue,在内部通过不断地取出msg,分发给handler 处理。

    Looper 构造方法中创建了MessageQueue 的成员变量,

    1. 我们使用Looper前 需要调用 Looper.prepare()方法,且只允许prepare一次。 可以看下源码
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
    
    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));
    }
    

    明显可以看到.此静态方法创建了一个Looper对象,放入sThreadLocal中,且多次调用的话会直接抛出异常

    即问题4的答案,通过ThradLocal 进行保证每个线程内只存在一个looper

    1. 通过Looper.loop() 函数来启动 MessageQueue,进行事件处理,loop() 内部是一个死循环,
      节选一下源码 主流程如下:
        public static void loop() {
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            final MessageQueue queue = me.mQueue;
    
            //进入死循环
            for (;;) {
               //1. 不断的取出数据,核心
                Message msg = queue.next(); // might block
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
    
                // 可以设置一个logPrinter 进行 调试
                final Printer logging = me.mLogging;
                if (logging != null) {
                    logging.println(">>>>> Dispatching to " + msg.target + " " +
                            msg.callback + ": " + msg.what);
                }
    
                final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
                final long dispatchEnd;
                Object token = null;
                if (observer != null) {
                    token = observer.messageDispatchStarting();
                }
                long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);
                try {
                   //2. 将msg 分发给 msg的target(Handler)
                    msg.target.dispatchMessage(msg);
                    if (observer != null) {
                        observer.messageDispatched(token, msg);
                    }
                    dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
                } catch (Exception exception) {
                    if (observer != null) {
                        observer.dispatchingThrewException(token, msg, exception);
                    }
                    throw exception;
                } finally {
                    ThreadLocalWorkSource.restore(origWorkSource);
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
    
                msg.recycleUnchecked();
            }
        }
    

    可以看到loop 循环中,是不断地从MessageQueue中取出msg,然后进行分发的
    包装了一些监听分发的处理
    核心点即是 Message msg = queue.next()
    接着看 MessageQueue

    MessageQueue

      MessageQueue 是整个机制的核心点,那它是怎么创建的呢?
    前面说到每个线程只会存在一个Looper,在Looper创建的时候会new一个MessageQueue对象,与当前线程绑定

       //是否支持退出,默认是true支持、主线程不支持
        private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    
    

    逻辑流程

    如上的几个类的大致说明,看下实际使用中的业务逻辑,在源码中的实现
    

    1. 插入消息

    前面查看handler的源码我们知道发送消息最后都调用到了MessageQueue的enqueueMessage方法
    下面为源码看下具体是怎么插入到队列中的

        //Handler 发送消息,实际还是通过持有的 MessageQueue 进行插入逻辑
        public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
            if (delayMillis < 0) {
                delayMillis = 0;
            }
            return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
        }   
        private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
                long uptimeMillis) {
            msg.target = this;
            msg.workSourceUid = ThreadLocalWorkSource.getUid();
    
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    
        //MessageQueue
        boolean enqueueMessage(Message msg, long when) {
           //插入消息禁止无目标的,同步屏障不是通过此方法设置
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
    
            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;//是否需要唤醒
                //1.头msg为空,或者要插入的msg时间戳小于队列头的.直接放在队列头
                if (p == null || when == 0 || when < p.when) {
                    // New head, wake up the event queue if blocked.
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    //2. 1 不满足的话,则很明显是要插入到队列中,
                    // 如果当前是同步屏障且该消息是异步消息则默认是需唤醒
                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                    Message prev;
                    //遍历找出当前应该插入的位置,从前往后
                    for (;;) {
                        prev = p;
                        p = p.next;
                        //到达链尾或者找到了比该msg更后的
                        if (p == null || when < p.when) {
                            break;
                        }
                        //如果是插入到队列中的其他的异步msg的后面,则不需要唤醒,因为前面的唤醒了
                        if (needWake && p.isAsynchronous()) {
                            needWake = false;
                        }
                    }
                    //3.插入到 prev 和 p 之间
                    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;
        }
    

    PS: 同步屏障

    频繁的提到了同步屏障这个东西,这个是什么呢,怎么插入?
    整体讲同步屏障是一个特殊的Msg,设置之后会阻止读取同步的msg,只允许分发异步msg,
    通过postSyncBarrier()设置,removeSyncBarrier移除,@hide方法,
    此方法慎用,如果 同步屏障未移除那么就无法处理业务事件了,相当于无反应了,且不会ANR

        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) {
                final int token = mNextBarrierToken++;//屏障当前的值,每次+1,移除时需要对应的token
                //1.构建同步屏障的msg
                final Message msg = Message.obtain();
                msg.markInUse();
                msg.when = when;
                msg.arg1 = token;
    
                Message prev = null;
                Message p = mMessages;
                //2.设置同步屏障的位置,prev 和 p之间
                // 依据when 进行插入
                if (when != 0) {
                    while (p != null && p.when <= when) {
                        prev = p;
                        p = p.next;
                    }
                }
                //3.插入
                if (prev != null) { // invariant: p == prev.next
                    msg.next = p;
                    prev.next = msg;
                } else {
                    msg.next = p;
                    mMessages = msg;
                }
                return token;
            }
        }
    
    

    为什么需要设置呢?
    同步屏障最常的使用地点就在ViewRootimpl里,在scheduleTraversals方法中设置,此方法熟悉的娃肯定知道是更新UI时会被调用到的.很简单的可以联想到奥 设置同步屏障是为了保证UI的更新
    例如: 当前消息队列已经充满了很多msg,等待处理,此时 vsync信号到来需要更新界面,总不可能让UI更新的消息排队吧,那样界面就卡主了.
    看下scheduleTraversals的源码流程也很容易发现此点。Choreographer.postCallback发送的是异步的msg

    2. 取出消息

    通过接下来查看关键的next方法,调用代码查看之前的Looper.loop()

        Message next() {
            // Return here if the message loop has already quit and been disposed.
            // This can happen if the application tries to restart a looper after quit
            // which is not supported.
            final long ptr = mPtr;
            if (ptr == 0) {
                return null;
            }
    
            int pendingIdleHandlerCount = -1; // -1 only during first iteration
            int nextPollTimeoutMillis = 0; //下一次取消息的时间间隔
    
            //1.que的死循环
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
                //2.native 方法,会阻塞,底层通过epoll进行阻塞,在用msg输入时唤醒,或者等待时间到达时
                nativePollOnce(ptr, nextPollTimeoutMillis);
    
                synchronized (this) {
    
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    //3.如果当前 message 是同步栅栏
                    // 遍历寻找下一个异步的 message,赋值给 msg
                    if (msg != null && msg.target == null) {
                        do {
                           //prevMsg 为 异步msg前的一个同步message
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    //4.当前的msg不为空(将要被处理的msg)
                    if (msg != null) {
                        //4.1 当前 message 的时间,还未到,计算需要等待的时间
                        if (now < msg.when) {
                            // Next message is not ready.  Set a timeout to wake up when it is ready.
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        //4.2 当前 message的 时间到了,返回此msg给looper分发
                        } else {
                            mBlocked = false;
                            //说明msg是异步消息,取出msg
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                //当前msg的下一个msg,mMessages
                                mMessages = msg.next;
                            }
                            //断链
                            msg.next = null;
                            if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                            msg.markInUse();
                            return msg;
                        }
                    } else {
                        //5.找不到msg,消息队列空了或者这个栅栏后,没有异步msg了
                        nextPollTimeoutMillis = -1;
                    }
                    // Process the quit message now that all pending messages have been handled.
                    if (mQuitting) {
                        dispose();
                        return null;
                    }
    
                    // If first time idle, then get the number of idlers to run.
                    // Idle handles only run if the queue is empty or if the first message
                    // in the queue (possibly a barrier) is due to be handled in the future.
                    if (pendingIdleHandlerCount < 0
                            && (mMessages == null || now < mMessages.when)) {
                        pendingIdleHandlerCount = mIdleHandlers.size();
                    }
                    if (pendingIdleHandlerCount <= 0) {
                        // No idle handlers to run.  Loop and wait some more.
                        mBlocked = true;
                        continue;
                    }
                    //线程空闲时,交给IdleHandler去处理。
                    if (mPendingIdleHandlers == null) {
                        mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                    }
                    mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
                }
    
                // Run the idle handlers.
                // We only ever reach this code block during the first iteration.
                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);
                        }
                    }
                }
    
                // Reset the idle handler count to 0 so we do not run them again.
                pendingIdleHandlerCount = 0;
    
                // While calling an idle handler, a new message could have been delivered
                // so go back and look again for a pending message without waiting.
                nextPollTimeoutMillis = 0;
            }
        }
    

    从源码中 很容易的可以看到,next方法也是一个死循环方法,整体作用是不断地循环取出msg(交给Looper分发出去)

    关键点1
    取出msg的时候会通过native方法进行阻塞调用
    nativePollOnce 通过Linux的底层 epoll,监听文件描述符的IO事件,msg插入时会通过nativeWake也会操作文件的描述符
    epoll
    其中通过传入的参数进行不同的等待

    MessageQueue阻塞nextPollTimeoutMillis毫秒的时间。
    1.如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
    2.如果nextPollTimeoutMillis=0,不会阻塞,立即返回
    3.如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
    

    关键点2
    成员变量mMessages指代队列中第一个msg(代表将要被处理的msg)

    相关文章

      网友评论

          本文标题:Handler 源码解析

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