美文网首页
Handler 同步屏障造成ANR分析

Handler 同步屏障造成ANR分析

作者: 馒Care | 来源:发表于2022-09-20 16:03 被阅读0次

问题

Handler同步屏障是否会导致ANR?

结论

同步屏障的使用有可能会导致ANR

分析

这里简单理解就是Choreographer每16ms回调doFrame,内部原理参考屏幕刷新机制

源码分析 ViewRootImpl#invalidate#scheduleTraversals

ViewRootImpl#invalidate#scheduleTraversals

@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            //通过Handler发送一个同步屏障到消息队列中
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //通过Choreographer发送callBack,等待垂直同步信号vsync回调doFrame
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

 void unscheduleTraversals() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            //移除同步屏障消息
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            mChoreographer.removeCallbacks(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }
    //doFrame回调后,调用doTraversal方法
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            //移除同步屏障消息
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }

            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }

从上述可以源码可以分析得知,我们在调用View刷新的时候,会调用到View#invalid,从而触发到ViewRootImpl#scheduleTraversals,继而会添加同步屏障到消息队列,来阻塞MessageQueue的普通消息,等待mTraversalRunnable#Choreographer#doFrame()回调后,移除掉当前的同步屏障,让其他普通消息,能够正常消费。

那么MessageQueue是如何判断是否需要阻塞的?MessageQueue#next

  Message next() {
          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());
                }
  }
}

通过源码可知,MessageQueue在轮训消息的时候,执行next()方法,会不断去获取msg对象,如果msg.target ==null,那么它就是屏障,需要循环遍历,一直往后找到第一个异步的消息。同样的,当我们移除掉同步屏障之后,也就不会执行当前判断体内,如下源码分析

//移除同步屏障消息
 mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
MessageQueue####
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) {
          //从消息链表中移除
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();

    }
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 = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }

重点看这里,移除屏障消息。
if (prev != null) {
prev.next = p.next;
needWake = false;
} 。
到此基础的同步屏障理论基本串联起来,我们正常在使用View更新的时候,都会走到同步屏障里面来,为了保障消息的优先级。

  • 到此也可以分析一个问题。如果同步屏障的消息没有及时处理掉,是因为Choreographer#doFrame没有及时回调,导致后续的普通消息被阻塞,没有在5S内消费掉,从而产生ANR。

需要注意的地方

  1. 不要在子线程做view#invalid的操作,根据上述源码,我们可以知道,控制同步屏障的添加跟移除是通过mTraversalScheduled这个变量来控制的,会出现多线程的异常,严重的可能导致,多线程多次调用iew#invalid,导致同步屏蔽被多次添加到队列中,但是确没有完全移除。这就是谷歌推荐我们如果需要在子线程更新UI。需要使用postInvalidate。

相关文章

  • Handler 同步屏障造成ANR分析

    问题 Handler同步屏障是否会导致ANR? 结论 同步屏障的使用有可能会导致ANR 分析 什么是同步屏障同步屏...

  • 关于Handler同步屏障你可能不知道的问题

    关于Handler同步屏障你可能不知道的问题 前言 很高兴遇见你 ~ 这篇文章主要讲Handler中的同步屏障问题...

  • Handler异步消息与同步屏障

    Handler Message 在Handler中,大致分为3种Message,分别是同步消息,异步消息和同步屏障...

  • Handler同步屏障

    平时看博客或者学知识,学到的东西比较零散,没有独立的知识模块概念,而且学了之后很容易忘。于是我建立了一个自己的笔记...

  • Handler同步屏障

    参考链接:https://www.jianshu.com/p/bc79cc25829a[https://www.j...

  • Handler同步屏障机制

    绘制流程中窥视handler同步屏障 在Android的绘制流程中,ViewRootImpl这个类发挥了非常重要的...

  • Android中同步屏障的应用及简析

    同步屏障的应用 ViewRootImpl.scheduleTraversals()里发送了Handler消息,最终...

  • Android 源码分析 - Handler的同步屏障机制

      自认为对Handler的机制还比较熟悉,可是最近在洋神的wanAndroid上面看到一个关于Handler同步...

  • Handler中的同步屏障

    同步屏障 首先需要发送一个特殊消息作为屏障消息,当消息队列检测到了这种消息后,就会从这个消息开始,遍历后续的消息,...

  • Android-Handler同步屏障

    一、消息机制之同步屏障 消息机制的同步屏障,其实就是阻碍同步消息,只让异步消息通过。而开启同步屏障的方法就是调用下...

网友评论

      本文标题:Handler 同步屏障造成ANR分析

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