android+刷新界面代码,源码分析_Android UI何时刷新_Choreographer

requestLayout和invalidate都干了些什么

之前我们在分析Activity是如何显示的时候,看到它调用了requestLayout然后走了重新绘制流程,其实我们在自定义View时经常用到requestLayout,invalidate等方法,我们调用它们的目的就是告诉系统我们要刷新下界面,但是实际上是他们去刷新界面吗?下面我们来看下:

我们之前分析过当我调用requestLayout的时候不管你在哪调最后都会传递到ViewRootImp中,由它来统一调用,其实invalidate也是一样。其实关于页面绘制有关的操作最后都是通过ViewRootImp来实现的。

我们看下ViewRootImp中的requestLayout和invalidate代码:

@Override

public void requestLayout() {

if (!mHandlingLayoutInLayoutRequest) {

checkThread();

mLayoutRequested = true;

scheduleTraversals();

}

}

void invalidate() {

mDirty.set(0, 0, mWidth, mHeight);

if (!mWillDrawSoon) {

scheduleTraversals();

}

}

他们两个都调用了scheduleTraversals()翻译过来的意思就是安排遍历的意思。

其实我们只要留心会发现很多地方都调了scheduleTraversals();

@Override

public void requestChildFocus(View child, View focused) {

if (DEBUG_INPUT_RESIZE) {

Log.v(mTag, "Request child focus: focus now " + focused);

}

checkThread();

scheduleTraversals();

}

@Override

public void clearChildFocus(View child) {

if (DEBUG_INPUT_RESIZE) {

Log.v(mTag, "Clearing child focus");

}

checkThread();

scheduleTraversals();

}

@Override

public void recomputeViewAttributes(View child) {

checkThread();

if (mView == child) {

mAttachInfo.mRecomputeGlobalAttributes = true;

if (!mWillDrawSoon) {

scheduleTraversals();

}

}

}

@Override

public void requestFitSystemWindows() {

checkThread();

mApplyInsetsRequested = true;

scheduleTraversals();

}

非常多我就不一一贴出来了,大体能看出来都是些跟页面显示状态有关的。所以我们猜测下 scheduleTraversals();他可能就是我们能刷新页面的关键。

总结下:在各处调用的invalidate和requestLayout最终都是调ViewRootImp中的 scheduleTraversals()方法

scheduleTraversals

下面看scheduleTraversals()的代码

void scheduleTraversals() {

if (!mTraversalScheduled) {

mTraversalScheduled = true;

mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

mChoreographer.postCallback(

Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

if (!mUnbufferedInputDispatch) {

scheduleConsumeBatchedInput();

}

notifyRendererOfFramePending();

pokeDrawLockIfNeeded();

}

}

看到里面有个postCallback方法,并有个mTraversalRunnable看名字可以猜应该是执行遍历的一个线程。不过一看这个就是个Handler那种是等待被执行的而非立即执行。我们先看下这个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();这个方法干了些什么我们在源码分析--Activity是如何显示的?中已经分析了大约800行代码核心如下:

private void performTraversals(){

...

performMeasure();

...

performLayout();

...

performDraw();

...

}

正是我们熟悉的三步走,所以performTraversals()是实现页面刷新的逻辑,但是很明显这个刷新是等待被通知的,那么何时被刷新呢?

mChoreographer.postCallback(

Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

我们看到这个Choreographer.CALLBACK_TRAVERSAL,事件是被Choreographer Post的,所以Choreographer应该就是通知我们何时刷新的类。

Choreographer

我们可以看下这个类的注释:Coordinates the timing of animations, input and drawing。

我翻译为控制输入、动画。绘制的时机。

可以看出这个类是告诉我们何时去执行输入、动画和绘制的。

还有一个注释也很重要:

The choreographer receives timing pulses (such as vertical synchronization) from the display subsystem then schedules work to occur as part of rendering the next display frame.

下面是个人的翻译:

Choreographer从显示的子系统中接受到类似垂直同步的时间脉冲,然后安排在下一帧中要呈现的工作。

这个注释告诉我们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) {

if (action == null) {

throw new IllegalArgumentException("action must not be null");

}

if (callbackType < 0 || callbackType > CALLBACK_LAST) {

throw new IllegalArgumentException("callbackType is invalid");

}

postCallbackDelayedInternal(callbackType, action, token, delayMillis);

}

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

}

}

}

这里有个是否立即执行的判断,如果立即执行则走scheduleFrameLocked(now);否则使用Handler在等待执行,我们搜下MSG_DO_SCHEDULE_CALLBACK可以找到:

case MSG_DO_SCHEDULE_CALLBACK:

doScheduleCallback(msg.arg1);

break;

void doScheduleCallback(int callbackType) {

synchronized (mLock) {

if (!mFrameScheduled) {

final long now = SystemClock.uptimeMillis();

if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {

scheduleFrameLocked(now);

}

}

}

}

所以都是走scheduleFrameLocked(now);区别在于是否立即执行。接着看scheduleFrameLocked()

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.

if (isRunningOnLooperThreadLocked()) {

scheduleVsyncLocked();

} else {

Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);

msg.setAsynchronous(true);

mHandler.sendMessageAtFrontOfQueue(msg);

}

} else {

Message msg = mHandler.obtainMessage(MSG_DO_FRAME);

msg.setAsynchronous(true);

mHandler.sendMessageAtTime(msg, nextFrameTime);

}

}

}

USE_VSYNC:

// Enable/disable vsync for animations and drawing.

private static final boolean USE_VSYNC = SystemProperties.getBoolean(

"debug.choreographer.vsync", true);

所以if里面是true,至于false的代码我个人猜测是安排在下一帧再刷新。看还是看true都干了啥,这里跟上面一样不过是对是否为ui线程做了个判断,如果是ui线程立即执行 scheduleVsyncLocked();否则通过Handler切换回ui线程,并将这个事件放在队列最前方然后执行scheduleVsyncLocked();

private void scheduleVsyncLocked() {

mDisplayEventReceiver.scheduleVsync();

}

/**

* Schedules a single vertical sync pulse to be delivered when the next

* display frame begins.

*/

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

}

}

nativeScheduleVsync方式是native的目前看不了,所以下面我们就不跟了。不过我们可以看下scheduleVsync()这个方法的注释:Schedules a single vertical sync pulse to be delivered when the next display frame begins.,我个人翻译为:安排一个垂直同步脉冲在下一次显示帧开始时发送。

其实就是在下一帧的时候安排一个垂直同步脉冲(Vsync)给我,这个我是谁呢?就是这个方法所属的类啊

DisplayEventReceiver。一看就名字:显示事件的接收者。

先总结下:postCallback的核心就是让DisplayEventReceiver注册了个Vsync的通知。

DisplayEventReceiver

上面看到了我们注册了对Vsync的监听,那么在哪接收Vsync呢

我们看到Choreographer中有个FrameDisplayEventReceiver里面重写了onVsync()和run()方法。

private final class FrameDisplayEventReceiver extends DisplayEventReceiver

implements Runnable {

private boolean mHavePendingVsync;

private long mTimestampNanos;

private int mFrame;

public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {

super(looper, vsyncSource);

}

@Override

public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {

.......

Message msg = Message.obtain(mHandler, this);

msg.setAsynchronous(true);

mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);

}

@Override

public void run() {

mHavePendingVsync = false;

doFrame(mTimestampNanos, mFrame);

}

}

我们可以看到当我们接收到Vsync消息后,通过Handler执行了run()里面的代码。那么我们接下来就看doFrame()中做了什么:

void doFrame(long frameTimeNanos, int frame) {

.......

try {

Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");

AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

mFrameInfo.markInputHandlingStart();

doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

mFrameInfo.markAnimationsStart();

doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);

mFrameInfo.markPerformTraversalsStart();

doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);

} finally {

AnimationUtils.unlockAnimationClock();

Trace.traceEnd(Trace.TRACE_TAG_VIEW);

}

....

}

除去一些计算操作,我们看到它主要执行了几个doCallBack,而里面有个Choreographer.CALLBACK_ANIMATION就是PostCallBack里面的。我们看下doCallback();

void doCallbacks(int callbackType, long frameTimeNanos) {

CallbackRecord callbacks;

.....

callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(

now / TimeUtils.NANOS_PER_MS);

.....

try {

Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);

for (CallbackRecord c = callbacks; c != null; c = c.next) {

....

c.run(frameTimeNanos);

}

} finally {

synchronized (mLock) {

mCallbacksRunning = false;

do {

final CallbackRecord next = callbacks.next;

recycleCallbackLocked(callbacks);

callbacks = next;

} while (callbacks != null);

}

Trace.traceEnd(Trace.TRACE_TAG_VIEW);

}

}

doCallbacks就是将CallbackQueues中的的CallbackRecord一个个取出来并执行run()方法,

public void run(long frameTimeNanos) {

if (token == FRAME_CALLBACK_TOKEN) {

((FrameCallback)action).doFrame(frameTimeNanos);

} else {

((Runnable)action).run();

}

}

run()其实就是执行了postCallback放进去的mTraversalRunnable。

现在在回看postCallback:

mChoreographer.postCallback(

Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

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) {

.....

mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

....

}

public void addCallbackLocked(long dueTime, Object action, Object token) {

CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);

.....

}

private CallbackRecord obtainCallbackLocked(long dueTime, Object action, Object token) {

CallbackRecord callback = mCallbackPool;

if (callback == null) {

callback = new CallbackRecord();

} else {

mCallbackPool = callback.next;

callback.next = null;

}

.......

}

ViewRootImp在执行scheduleTraversals()方法时就是把包含Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable的信息封装到了CallbackRecord的对象中,并添加到CallbackQueue,现在doCallback中将其取出然后执行run()方法,它的run()就是执行 doTraversal();

final class TraversalRunnable implements Runnable {

@Override

public void run() {

doTraversal();

}

}

至此基本就串上了。

总结

我们调用View的requestLayout或者invalidate时,最终都会触发ViewRootImp执行scheduleTraversals()方法。这个方法中ViewRootImp会通过Choreographer来注册个接收Vsync的监听,当接收到系统体层发送来的Vsync后我们就执行doTraversal()来重新绘制界面。通过上面的分析我们调用invalidate等刷新操作时,系统并不会立即刷新界面,而是等到Vsync消息后才会刷新页面。

当然Choreographer还会通知动画输入等其他事件。至于关于Vsync的分析可以参考一下博客:

破译Android性能优化中的16ms问题

Android垂直同步信号VSync的产生及传播结构详解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值