美文网首页
Android屏幕刷新机制(二)

Android屏幕刷新机制(二)

作者: 慕尼黑凌晨四点 | 来源:发表于2020-11-01 18:58 被阅读0次

Android屏幕刷新机制(二): invalidate()发生了什么

参考

文章完全参考自:Android 屏幕刷新机制,简化了部分代码逻辑,加了点自己的理解。

invalidate()

如果大家了解View体系里的相关知识应该知道,每个Activity 的根布局都是 DecorViewDecorView 的 parent 又是 ViewRootImpl,所以子view执行invalidate()方法时最终会引导到ViewRootImpl中来。

ViewRootImplinvalidate()方法:

void invalidate() {
    mDirty.set(0, 0, mWidth, mHeight);
    if (!mWillDrawSoon) {
        scheduleTraversals();
    }
}

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //看这句:postCallback
        mChoreographer.postCallback(
            // mTraversalRunnable
            Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

mChoreographer.postCallback()第二个参数传入了一个runnable对象,先看这个对象是啥。

TraversalRunnable

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }
        //看这里~
        performTraversals();

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

会跳转到performTraversals中来,这个方法是界面刷新的源头。这个方法使得ViewTree开始View的工作流程。performMeasure、performLayout、performDraw方法都在里面。

回过头再看这行代码中的这个方法:

mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

Choreographer.postCallback

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;
        //哦豁
        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,在这里出现了。上一篇不是不知道传进来的token、action是什么么?现在知道了。token是null,action是TraversalRunnable,所以屏幕发送VSync信号后,系统走的是TraversalRunnable.run()方法。

然后到了performTraversals中来,开始重绘界面。

invalidate()方法所做的,不过是向CallbackQueue中添入一个CallbackRecord

image-20201101151630877

scheduleFrameLocked

还没走完,下面还有几行。一个判断,看看scheduleFrameLocked方法。

if (dueTime <= now) {
    scheduleFrameLocked(now);
} else {
    //这个mHandler,便是我们久违的FrameHandler
    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);//2
    msg.arg1 = callbackType;
    msg.setAsynchronous(true);
    mHandler.sendMessageAtTime(msg, dueTime);
}

private void scheduleFrameLocked(long now) {
    if (!mFrameScheduled) {
        mFrameScheduled = true;
        if (USE_VSYNC) {
            // If running on the Looper thread, then schedule the vsync immediately,
            // otherwise post a message to schedule the vsync from the UI thread
            // as soon as possible.
            //翻译:如果在主线程(Looper)就立即计划(schedule)一个vsync,否则快马加鞭传信给主线程,
            //让主线程 马上(as soon as possible) 计划(schedule)一个vsync。
            if (isRunningOnLooperThreadLocked()) {
                scheduleVsyncLocked();
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);//1
                msg.setAsynchronous(true);
                mHandler.sendMessageAtFrontOfQueue(msg);
            }
        } else {
            final long nextFrameTime = Math.max(
                    mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
            Message msg = mHandler.obtainMessage(MSG_DO_FRAME);//0
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, nextFrameTime);
        }
    }
}

这里的mHandle,便是我们久违的FrameHandler。看样子是分不同的情况,传不同的信息(message)走FrameHandler了。官方的注释也写的很清楚,尽快计划一个VSync,虽然不知道计划VSync是什么鬼。

那就看代码:追踪scheduleVsyncLocked();会发现最终会走到DisplayEventReceiver.scheduleVsync()方法中来。

/**
 * Schedules a single vertical sync pulse to be delivered when the next
 * display frame begins.
 * 计划一个VSync脉冲,用于下一帧到来时的交付。
 */
@UnsupportedAppUsage
public void scheduleVsync() {
    if (mReceiverPtr == 0) {
        Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                + "receiver has already been disposed.");
    } else {
        nativeScheduleVsync(mReceiverPtr);
    }
}

可见最后在这里会注册一个监听器,表明已经有屏幕绘制的安排了,下一帧要用我绘制的帧。

再看看另外三种情况:

private final class FrameHandler extends Handler {
    public FrameHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case MSG_DO_FRAME:
                //开始制作下一帧
                doFrame(System.nanoTime(), 0);
                break;
            case MSG_DO_SCHEDULE_VSYNC:
                //走scheduleVsyncLocked,还是立即执行了
                doScheduleVsync();
                break;
                //做个判断,然后走scheduleFrameLocked,还是立即执行了
            case MSG_DO_SCHEDULE_CALLBACK:
                doScheduleCallback(msg.arg1);
                break;
        }
    }
}

void doScheduleVsync() {
    synchronized (mLock) {
        if (mFrameScheduled) {
            scheduleVsyncLocked();
        }
    }
}

void doScheduleCallback(int callbackType) {
    synchronized (mLock) {
        if (!mFrameScheduled) {
            final long now = SystemClock.uptimeMillis();
            if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
                scheduleFrameLocked(now);
            }
        }
    }
}

可见,除了MSG_DO_FRAME,其他的情况都是走向了scheduleVsync,注册了下一帧的监听。MSG_DO_FRAME的意思就是这一帧没有图像了,系统又doFrame循环一次去取下一帧的图像了。

总结

最后,放张流程图总结。


image.png

相关文章

网友评论

      本文标题:Android屏幕刷新机制(二)

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