我是靠谱客的博主 刻苦枕头,最近开发中收集的这篇文章主要介绍【Android】界面是如何刷新的流程Choreographer接收刷新信号总结相关内容,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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之间的关系

最后

以上就是刻苦枕头为你收集整理的【Android】界面是如何刷新的流程Choreographer接收刷新信号总结相关内容的全部内容,希望文章能够帮你解决【Android】界面是如何刷新的流程Choreographer接收刷新信号总结相关内容所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(36)

评论列表共有 0 条评论

立即
投稿
返回
顶部