• 【Android】界面是如何刷新的流程


    Choreographer接收刷新信号

    接收屏幕的垂直同步信号,通过Handler发送Message,垂直同步信号和我们常说的刷新率相关,一般的手机刷新率是60,意思是每秒发送60个垂直同步信号:

    #FrameDisplayEventReceiver 
    @Override
    public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
        Message msg = Message.obtain(mHandler, this);
        msg.setAsynchronous(true);
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }
    
    

    然后在Handler中处理Message,Message中what默认是0,因此发送Message的时候没有给what赋值:

    private final FrameHandler mHandler;
    private static final int MSG_DO_FRAME = 0;
    
    private final class FrameHandler extends Handler {
    
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DO_FRAME:
                    doFrame(System.nanoTime(), 0);
                    break;
            }
        }
    }
    
    

    依次调用各种事件类型,调用的顺序表示了各自的优先级:

    #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;
    
    void doFrame(long frameTimeNanos, int frame) {
        try {
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
        }
    }
    

    mCallbackQueues就是一个数组,数组中的每一个CallbackQueue,是一个事件的集合。

    例如:CALLBACK_TRAVERSAL = 3,那么mCallbackQueues的第四个集合存储的就是View刷新的信号。

    集合中的项是CallbackRecord实例,doCallbacks方法会遍历集合,从中取出符合计时条件的项,然后执行run方法:

    #Choreographer
    
    private final CallbackQueue[] mCallbackQueues;
    
    void doCallbacks(int callbackType, long frameTimeNanos) {
    	
        callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
               now / TimeUtils.NANOS_PER_MS);
    	
        try {
            for (CallbackRecord c = callbacks; c != null; c = c.next) {
                c.run(frameTimeNanos);
            }
        }
    }
    

    mCallbackQueues是在Choreographer构造方法中初始化的,初始化时就固定了size = 5

    public static final int CALLBACK_COMMIT = 4;
    
    private static final int CALLBACK_LAST = CALLBACK_COMMIT;
    
    private Choreographer(Looper looper, int vsyncSource) {
    
        mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
        for (int i = 0; i <= CALLBACK_LAST; i++) {
            mCallbackQueues[i] = new CallbackQueue();
        }
        
    }
    

    到现在我们了解到,应用接收到垂直同步信号之后,会遍历队列中的项。那么,这些项是什么时候加入到队列中的呢?

    CALLBACK_TRAVERSAL为例:

    ViewRootImpl中多处调用了scheduleTraversals,其中有我们非常熟悉的requestLayoutinvalidate等等
    在这里插入图片描述
    scheduleTraversals方法中,插入了CALLBACK_TRAVERSAL到队列中:

    #ViewRootImpl
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }
    
    public void postCallback(int callbackType, Runnable action, Object token) {
        postCallbackDelayed(callbackType, action, token, 0);
    }
    
    private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
    
        synchronized (mLock) {
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
        }
    }
    

    到这里,我们知道了队列中的项是如何添加进去的。

    那么CALLBACK_TRAVERSAL回调被执行后,做了哪些动作呢?

    CALLBACK_TRAVERSAL一起添加到队列中的,还有一个实例mTraversalRunnable

    #ViewRootImpl
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }
    

    mTraversalRunnable的创建和定义:

    #ViewRootImpl
    
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    

    doTraversal调用了performMeasure、 performLayout、performDraw、等方法。

    总结

    梳理一下整体流程:

    1. ViewRootImpl接收到更新界面请求
    2. ViewRootImpl添加回调到Choreographer队列中
    3. Choreographer接收到屏幕发送的同步信号,从队列中取出回调执行
    4. 回调执行ViewRootImpl的方法,调用View的相关方法,更新界面

    相关内容

    毫无疑问,上述流程是在主线程执行的。

    应用每16ms收到一次同步信号,意味着主线程的代码必须在16ms以内执行完成。

    超过16ms会发生丢帧。丢帧严重时,用户会感知到明显的卡顿。

    使用Profiler能够方便定位到丢帧位置:

    Android性能优化:使用Android Studio的Profiler分析函数执行时间

    View是谁?从哪里来的?
    ViewRootImpl、WindowManagerGlobal和WindowManager之间的关系

  • 相关阅读:
    托管网站需要知道的网站优化指标有哪些
    .NET开源全面方便的第三方登录组件集合 - MrHuo.OAuth
    单片机设计_RTC时钟(ACM32F403)
    反射的作用( 越过泛型检查 和 可以使用反射保存所有对象的具体信息 )
    每日一题 —— 882. 细分图中的可到达节点
    (仿牛客社区项目)Java开发笔记7.6:热帖排行
    大数据项目之电商数仓、业务数据介绍、电商系统表结构
    Not annotated parameter overrides @NonNullApi parameter
    Qt部署MQTT
    进程 线程 协程
  • 原文地址:https://blog.csdn.net/qq_23049111/article/details/126124949