双目摄像头的帧同步输入fsync信号_读源码长知识 | Android卡顿真的是因为”掉帧“?...

f2da66691fe9eb8227f3f3ba013e8cad.png
作者:唐子玄

Andriod 界面卡顿是因为掉帧,而掉帧是因为生产帧的速度跟不上消费帧的速度。

消费帧的速度与屏幕刷新率挂钩,屏幕就像连环画,若一秒播放 60 帧,消费一帧的速度为 1000/60 = 16.6 ms,即每 16.6 ms 屏幕就会去取下一帧的显示内容,若没取到,只能继续显示上一帧,画面就停滞了,这就称为“掉帧”,听上去好像丢失了无法找回的东西一样,其实它是形容“显示内容错过了一次显示机会”,描述的是屏幕硬件的一种行为。

屏幕为啥会没有取到显示内容?得从软件层找原因。带着这个问题,读一下 Framework 源码。

第一次看我文章的小伙伴可以关注一下我,顺便关注一下我的专栏:Android高级技术专栏 每天更新各种技术干货,分享更多最热程序员圈内事。
Android高级技术专栏​zhuanlan.zhihu.com
2705da2a4937d9039ff866ebd100449d.png

Choreographer

ViewRootImpl是一个 Activity 中所有视图的根视图,View 树的遍历就由它发起:

public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
    // 编舞者
    Choreographer mChoreographer;
    // 触发遍历 View 树
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            // 向 Choreographer 抛 View树遍历任务
            mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    
    // 遍历 View 树任务
    final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    }
    // 构建遍历 View 树任务实例
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
}

ViewRootImpl通过抛任务到Choreographer来触发 View 树遍历。

Choreographer是 android.view包下的一个类,字面意思是“编舞者”,隐含着“需要将两者同步”的意思,编舞即是将动作和节奏同步。而Choreographer是将"绘制内容"和"垂直同步信号"同步。

存放绘制任务

public final class Choreographer {
    // 输入任务
    public static final int CALLBACK_INPUT = 0;
    // 动画任务
    public static final int CALLBACK_ANIMATION = 1;
    // view树遍历任务
    public static final int CALLBACK_TRAVERSAL = 2;
    // COMMIT任务
    public static final int CALLBACK_COMMIT = 3;
    // 暂存任务的链式数组
    private final CallbackQueue[] mCallbackQueues;
    // 主线程消息处理器
    private final FrameHandler mHandler;
    
    // 抛绘制任务
    public void postCallback(int callbackType, Runnable action, Object token) {
        postCallbackDelayed(callbackType, action, token, 0);
    }

    // 延迟抛绘制任务
    public void postCallbackDelayed(int callbackType,
            Runnable action, Object token, long delayMillis) {
        ...
        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;
            // 1. 将绘制任务根据类型暂存在链式结构中
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            // 2. 订阅下一个垂直同步信号
            if (dueTime <= now) {
                // 立刻订阅下一个垂直同步信号
                scheduleFrameLocked(now);
            } else {
                // 在未来的某个时间点订阅垂直同步信号
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }
    
    // 主线程消息处理器
    private final class FrameHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                ...
                case MSG_DO_SCHEDULE_CALLBACK:
                    // 在未来时间点订阅垂直同步信号
                    doScheduleCallback(msg.arg1);
                    break;
            }
        }
    }
    
    void doScheduleCallback(int callbackType) {
        synchronized (mLock) {
            if (!mFrameScheduled) {
             
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值