美文网首页Android进阶之路
Framework的进程内通信,搞定Handler消息机制的方方

Framework的进程内通信,搞定Handler消息机制的方方

作者: 码农的地中海 | 来源:发表于2022-06-06 20:07 被阅读0次

    简介:

    handler机制,在android中提供了一种异步回调机制Handler,使用它,我们可以在完成一个很长时间的任务后做出相应的通知。

    每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。典型应用就是在子线程中更新 UI 线程。那么,很显然 Handler 的作用就是用于线程间通信。

    QQ截图20220606192656.png

    Handler用途

    Handler主要有两个主要用途:

    • 在未来的某个时间点调度messages和runnables的执行
    • 将要在不同线程上执行的操作加入队列

    当你的应用程序被创建出来的时候,主线程会专门运行一个message queue来管理最顶级的应用对象(如activities, broadcast receivers,等等)以及它们创建的任何其它窗口。你可以创建你自己的线程,通过Handler来与主线程建立联系

    Handler源码解析异步消息处理

    Message next() {
        ...省略部分代码...
        for (; ; ) {
           ...省略部分代码...
            synchronized (this) {
                Message prevMsg = null;
                Message msg = mMessages;
                //这里循环找异步消息 msg.isAsynchronous
                if (msg != null && msg.target == null) {
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                ...省略部分代码...
            }
        }
    }
    复制代码
    

    我们来看看msg.isAsynchronous代码:

    public boolean isAsynchronous() {
        return (flags & FLAG_ASYNCHRONOUS) != 0;
    }
    static final int FLAG_ASYNCHRONOUS = 1 << 1;
    复制代码
    

    那么我们来看看在哪里添加了这个flag:

    public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
    }
    复制代码
    

    现在我们看到Message.setAsynchronous(boolean async)这个方法用来控制是否是异步消息,在哪里调用了呢, 我们先来看View.requestLayout():

    public void requestLayout() {
        ...省略部分代码...
        //这里的mParent是自己的父View,如果view本身已经是最顶层view,则mParent就是ViewRootImpl
        if (mParent != null && !mParent.isLayoutRequested()) {
            mParent.requestLayout();
        }
        ...省略部分代码...
    }
    复制代码
    

    这里会层层向上调用mParent.requestLayout(),直到调用到ViewRootImpl.requestLayout()为止

    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread(); //检查当前线程
            mLayoutRequested = true; //更新标记
            scheduleTraversals(); //开始遍历
        }
    }
    复制代码
    void checkThread() {
        //这里检查线程是否是mThread,不是的话就抛出异常,mThread一般是UI线程
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
        }
    }
    复制代码
    public boolean mTraversalScheduled;
    int mTraversalBarrier;
    Choreographer mChoreographer;
    void scheduleTraversals() {
        if (!mTraversalScheduled) { //不在执行中才执行
            mTraversalScheduled = true; //更新标记为执行中
            //向mHandler的MessageQueue中添加一个同步屏障,一个同步屏障也是一个Message,但是这个Message的target是null,并且后面会有一个异步消息AsynchronousMessage
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //这里是重点!!!,通过Choreographer来post一个消息,//TAG1: CALLBACK_TRAVERSAL,要记住这个TAG
            mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            ...省略部分代码...
        }
    }
    复制代码
    

    我们先来看MessageQueue.postSyncBarrier():

    public int postSyncBarrier() {
        //这里使用当前时间作为参数传递进去
        return postSyncBarrier(SystemClock.uptimeMillis());
    }
    
    private int postSyncBarrier(long when) {
        synchronized (this) {
            final int token = mNextBarrierToken++; //token增加1
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when; //msg.when就是当前时间
            msg.arg1 = token; //arg1就是token
            //注意!!!这个msg没有target,也就是msg.target=null
    
            //循环遍历,将这个同步屏障msg插入到队列
            Message prev = null;
            Message p = mMessages;
    
            //遍历查找when之前的消息
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            //插入到队列
            if (prev != null) {
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }
    复制代码
    

    再来看看removeSyncBarrier():

    //这个token就是我们上面保存的
    public void removeSyncBarrier(int token) {
        synchronized (this) {
            //遍历删除这个token对应的msg
            Message prev = null;
            Message p = mMessages;
            //只要p.target!=null || p.arg1!=token就一直找,因为上面我们分析了同步屏障的target=null,并且arg1=token
            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);
            }
        }
    }
    复制代码
    

    然后看mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);第一个参数是个int,第二个参数mTraversalRunnable是个Runnable,代码如下:

    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    
    void doTraversal() {
        if (mTraversalScheduled) { //不在执行中才执行
            mTraversalScheduled = false; //更新标记为执行中
            //从mHandler的MesssageQueue中移除同步屏障,还记得刚刚添加同步屏障的代码吗
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            //开始遍历布局
            performTraversals();
        }
    }
    
    //这个方法对view进行了测量,布局和绘制,我们后面会细讲
    private void performTraversals() {
        ......
        performMeasure();
        ......
        performLayout();
        ......
        performDraw();
        ......
    }
    复制代码
    

    我们来看下Choreographer类的部分关键代码:

    class Choreographer {
        public static final int CALLBACK_INPUT = 0;
        public static final int CALLBACK_ANIMATION = 1;
        public static final int CALLBACK_INSETS_ANIMATION = 2;
        public static final int CALLBACK_TRAVERSAL = 3; //这是刚刚的第一个参数
        public static final int CALLBACK_COMMIT = 4;
        private static final int CALLBACK_LAST = CALLBACK_COMMIT;
        private final CallbackQueue[] mCallbackQueues;
        private final FrameHandler mHandler;
        private final FrameDisplayEventReceiver mDisplayEventReceiver;
    
        private Choreographer(Looper looper, int vsyncSource) {
            mLooper = looper;
    
            mHandler = new FrameHandler(looper);
    
            //这里初始化mDisplayEventReceiver
            mDisplayEventReceiver = USE_VSYNC
                    ? new FrameDisplayEventReceiver(looper, vsyncSource)
                    : null;
            mLastFrameTimeNanos = Long.MIN_VALUE;
    
            mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
    
            //这里对mCallbackQueues进行初始化,直接new出来一个长度为5的数组,并且初始化5个元素,为什么长度是5?因为上面说了CALLBACK_LAST=4
            mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
            for (int i = 0; i <= CALLBACK_LAST; i++) {
                mCallbackQueues[i] = new CallbackQueue();
            }
        }
    
        //这就是刚刚调用的方法
        public void postCallback(int callbackType, Runnable action, Object token) {
            postCallbackDelayed(callbackType, action, token, 0); //第四个参数是0
        }
    
        public void postCallbackDelayed(int callbackType,Runnable action, Object token, long delayMillis) {
            if (action == null) {
                throw new IllegalArgumentException("action must not be null");
            }
            if (callbackType < 0 || callbackType > CALLBACK_LAST) {
                throw new IllegalArgumentException("callbackType is invalid");
            }
    
            postCallbackDelayedInternal(callbackType, action, token, delayMillis);
        }
    
        //最终调到这里
        private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) {
            synchronized (mLock) {
                final long now = SystemClock.uptimeMillis(); //获取当前时间
                final long dueTime = now + delayMillis; //delayMillis = 0,所以dueTime = now
                //这是个数组 callbackType是CALLBACK_TRAVERSAL,dueTime=0,action就是那个mTraversalRunnable
                //这里就是将这个mTraversalRunnable保存在CALLBACK_TRAVERSAL对应的集合里了
                mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
    
                if (dueTime <= now) { //true
                    scheduleFrameLocked(now); //跑这里
                } else {
                    //否则通过Handler发送出去
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                    msg.arg1 = callbackType;
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtTime(msg, dueTime);
                }
            }
        }
    
        //接着看
        private void scheduleFrameLocked(long now) {
            if (!mFrameScheduled) {
                mFrameScheduled = true;
                if (USE_VSYNC) {
                    //看这里
                    if (isRunningOnLooperThreadLocked()) {
                        scheduleVsyncLocked();
                    } else {
                        Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); //msg.what = MSG_DO_SCHEDULE_VSYNC
                        msg.setAsynchronous(true); //标记为异步消息!! 前面我们加了个同步屏障了的
                        mHandler.sendMessageAtFrontOfQueue(msg); //发送,这个mHandler是FrameHandler
                    }
                } else {
                    final long nextFrameTime = Math.max(mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
                    Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtTime(msg, nextFrameTime);
                }
            }
        }
    }
    复制代码
    

    接着来看FrameHandler的代码:

    private final class FrameHandler extends Handler {
        public FrameHandler(Looper looper) {
            super(looper);
        }
    
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DO_FRAME:
                    doFrame(System.nanoTime(), 0);
                    break;
                case MSG_DO_SCHEDULE_VSYNC: //跑到了这里
                    doScheduleVsync();
                    break;
                case MSG_DO_SCHEDULE_CALLBACK:
                    doScheduleCallback(msg.arg1);
                    break;
            }
        }
    }
    复制代码
    

    接着看

    void doScheduleVsync() {
        synchronized (mLock) {
            if (mFrameScheduled) {
                scheduleVsyncLocked();
            }
        }
    }
    
    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();
    }
    
    //来看下scheduleVsync方法:
    public void scheduleVsync() {
        if (mReceiverPtr == 0) {
            Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                    + "receiver has already been disposed.");
        } else {
            //这里直接调用了native的方法,native调用完成会回到
            nativeScheduleVsync(mReceiverPtr);
        }
    }
    复制代码
    

    native完事后会回调到FrameDisplayEventReceiver.onVsync()里面:

    private final class FrameDisplayEventReceiver extends DisplayEventReceiverimplements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;
    
        public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
            super(looper, vsyncSource, CONFIG_CHANGED_EVENT_SUPPRESS);
        }
    
        @Override
        public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
        long now = System.nanoTime();
        if (timestampNanos > now) {
            timestampNanos = now;
        }
    
        if (mHavePendingVsync) {
            Log.w(TAG, "Already have a pending vsync event.  There should only be "
                    + "one at a time.");
        } else {
            mHavePendingVsync = true;
        }
    
        mTimestampNanos = timestampNanos;
        mFrame = frame;
        Message msg = Message.obtain(mHandler, this); //这里的第二个参数是this,意味着msg.callback = this,那么就会跑到自己的run函数 
        msg.setAsynchronous(true);
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }
    
        @Override
        public void run() {
            //跑到这里了
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
    }
    复制代码
    void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
            if (!mFrameScheduled) {
                return; // no work to do
            }
        ...省略计算时间的代码...
        //下面开始执行事件顺序为: 1 输入事件 2 动画 3 布局
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
    
            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
    
            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
    
            mFrameInfo.markPerformTraversalsStart();
            //TAG: CALLBACK_TRAVERSAL,还记得这个TAG吗,我们看这里即可
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    
            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
    
    void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
            //这里根据callbackType取出所有事件,这里的type是:CALLBACK_TRAVERSAL
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {
                return;
            }
            mCallbacksRunning = true;
            ...省略部分代码...
        }
        try {
            //遍历取出所有事件,执行,还记得我们之前的那个:mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)吗?
            //第二个参数就是i一个i饿runnable,就会被执行,也就是执行到我们的traversal()里去了,最终就会去测量、布局、绘制
            for (CallbackRecord c = callbacks; c != null; c = c.next) {
                c.run(frameTimeNanos);
            }
        } finally {
            ...回收callback...
        }
    }
    复制代码
    

    通过以上分析我们了解到: Android在布局的时候使用了异步消息,为什么要使用异步消息呢,有什么好处,答案就是:异步消息会被优先处理,比如当前MessageQueue中有100个消息,这时候有个布局消息来了,正常的话要等待100个执行完,如果是异步消息,则可以优先执行,这样UI就不会因为消息过多而卡顿,这就是优点,我们再来看看MessageQueue.next的代码:

    Message next() {
        ...省略部分代码...
        for (;;) {
            ...省略部分代码...
            synchronized (this) {
                final long now = SystemClock.uptimeMillis(); //记录当前时间
                Message prevMsg = null;
                Message msg = mMessages; //取出第一条消息,也就是队头消息
    
                //msg.target == null! 上面分析过,这是一个同步屏障,而同步屏障后面一般会有一个人异步消息,同步屏障的目的就是为了标记后面有个异步消息的
                if (msg != null && msg.target == null) { //如果有同步屏障,那么就找异步消息
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous()); //果然在这里,只要不是异步消息,就一直找,直到找到为止
                }
    
                //开始处理消息
                if (msg != null) {
                    ...处理msg...
                } else {
                    // 标记为-1无限等待
                    nextPollTimeoutMillis = -1;
                }
                ...省略部分代码...
            }
            ...省略处理空闲消息的代码...
        }
    }
    复制代码
    

    上面省略了部分代码,主要分析了 异步消息是被优先执行的,而且异步消息都跟在同步屏障后面的

    1.为什么要把Looper放在ThreadLocal里面

    我们先来看怎么把Looper放在ThreadLocal里面的? Looper.prepare()

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    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));
    }
    复制代码
    

    ThreadLocal.set()

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    复制代码
    

    ThreadLocalMap.getMap() 可以看到Thread有一个成变量ThreadLocalMap

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    public class Thread implements Runnable {
        ThreadLocal.ThreadLocalMap threadLocals = null;
    
    }
    复制代码
    

    createMap() 这里直接创建一个ThreadLocalMap

    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    复制代码
    

    这里来总结一下存放Looper的逻辑

    • 1 每个线程都有个ThreadLocalMap的成员变量threadLocals,这个变量可以理解为一个HashMap,其中key是ThreadLocal,value是任意类型。
    • 2 Looper内部有个ThreadLocal静态成员变量sThreadLocal,在prepare()的时候,会先创建一个Looper,然后获取当前线程的ThreadLocalMap,并且把自自己的成员变量sThreadLocal作为key,把这个looper作为值存放进去,可以简单理解为:
    Looper looler = new Looper(); //创建一个looper
    ThreadLocalMap map = Thread.currentThread().getMap(); //获取当前线程的ThreadLocalMap
    map.set(sThreadLocal, looper); //将looper存入当前线程的ThreadLocalMap中
    复制代码
    

    我们再来看看是怎么从ThreadLocal里面获取Looper的?

    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
    复制代码
    

    很简单,直接使用ThreadLocal的get()函数,这里可以缕一下: 因为ThreadLocalMap是每个线程都有的成员变量,所以在线程A里面使用Looper.myLooper()得到的就是线程A的looper,线程B得到的就是线程B的looper,UI线程肯定就是UI线程的looper,所以就达到了线程切换的目的。

    2.ActivityThread.main()是在哪里被调用的

    答: 是在app进程被创建的时候调用的,app进程被创建的时候,会通过zygote来fork一个新进程作为app进程,然后通过反射调用ActivityThread的main方法,详细流程后面我们专门出一个章节来讲

    总结

    异步消息跟在同步屏障后面(不一定是紧临),异步消息会被优先执行,寻找异步消息的过程是先找同步屏障,如果有,就遍历next来找异步消息,同步屏障的特点是msg.target=null,异步消息的特点是isAsynchronous = true。 eg: 同步屏障是50,异步消息是100,同步屏障前面的1-49会先执行,然后执行到50的时候发现是同步屏障,然后就遍历后续消息,找到异步消息100,取出来执行,然后移除同步屏障50,在继续执行后面的消息51,以此类推

    使用ThreadLocal是为了将Looper保存在线程内部,从而达到跨线程的目的

    相关文章

      网友评论

        本文标题:Framework的进程内通信,搞定Handler消息机制的方方

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