Android进阶 -- 屏幕刷新机制

本文详细探讨了Android系统的屏幕刷新机制,从View的requestLayout方法开始,经过ViewRootImpl,Choreographer,再到DisplayEventReceiver,揭示了如何通过请求vsync信号触发UI绘制。在分析过程中,解释了屏幕卡顿的底层原因以及如何避免掉帧,帮助开发者理解如何保障Android应用的流畅性。
摘要由CSDN通过智能技术生成

关于屏幕卡顿,从表象来讲,是因为主线程有耗时操作,导致屏幕绘制掉帧,屏幕每16毫秒会刷新一次,也就是每秒刷新60次,人眼能感觉到的卡顿的帧率是每秒24帧。所以解决卡顿的一般处理方法就是将耗时操作放在子线程、减少View层级、多使用include merge viewstub标签等,来保障屏幕绘制的流畅。

不过再往深里看,会有这样一些问题:卡顿的底层原因是什么?如何理解16毫米刷新一次?假如界面没有更新操作,View会每16毫秒刷新一次吗?

带着这些问题,来看下面的文字。

1.View

View里的requestLayout方法,常用来主动请求UI的更新,我们从这个方法看起

public void requestLayout() {
    …
    if (mParent != null && !mParent.isLayoutRequested()) {
        mParent.requestLayout();
    }
    …
}

View的requestLayout方法实际上调用的是mParent的requestLayout方法,而mParent是ViewParent的实例,再来看ViewParent接口

/**
 * Defines the responsibilities for a class that will be a parent of a View.
 * This is the API that a view sees when it wants to interact with its parent.
 * 
 */
public interface ViewParent {
    /**
     * Called when something has changed which has invalidated the layout of a
     * child of this view parent. This will schedule a layout pass of the view
     * tree.
     */
    public void requestLayout();
}

这里的mParent.requestLayout最终会调用到ViewRootImpl的requestLayout方法,因为根View是DecorView,而DecorView的parent就是ViewRootImpl。

 

2.ViewRootImpl

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {
        checkThread();
        mLayoutRequested = true;
        scheduleTraversals();
    }
}

ViewRootImpl里的requestLayout方法只有4行代码,首先调用了 checkThread()方法,用来做线程的检查,点进去就看到了大名鼎鼎的异常信息

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

在看scheduleTraversals()方法

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true; 
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

mTraversalScheduled标志位,这个标志位为false时才可以刷新UI,设置这个标志位的原因是防止短时间多次调用requestLayout重复绘制。

mHandler.getLooper().getQueue().postSyncBarrier()这里发送了一条同步屏障消息,这时候消息队列中的同步消息不会被处理,而是优先处理异步消息。

mChoreographer.postCallback向Choreographer里提交了一个mTraversalRunnable任务,这个任务不会马上执行。

 

3.Choreographer

postCallback实际调用的方法是postCallbackDelayedInternal

private void postCallbackDelayedInternal(int callbackType,
        Object action, Object token, long delayMillis) {	
    …
    synchronized (mLock) {
        final long now = SystemClock.uptimeMillis();
        final long dueTime = now + delayMillis;
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        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);
        }
    }
}

addCallbackLocked 将任务添加到队列,不会马上执行。

scheduleFrameLocked 通常delayMillis是0 执行scheduleFrameLocked方法

private void scheduleFrameLocked(long now) {
    if (!mFrame
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值