【面试专题】Android屏幕刷新机制

(这边补充说一个面试题,属性动画更新时会回调onDraw吗?不会,因为它内部是通过AnimationHandler中的Choreographer机制来实现的更新,具体的逻辑,如果以后有时间的话可以写篇文章来说一说。)

业界一般通过Choreographer来监控应用的帧率。

(这个东西也是个面试题,会问你如何检测应用的帧率?你可以提一下Choreographer里面的FrameCallback,然后结合一些第三方库的实现具体说一下。)

View刷新的入口

Activity启动,走完onResume方法后,会进行window的添加。window添加过程会调用ViewRootImpl的setView()方法, setView()方法会调用requestLayout()方法来请求绘制布局,requestLayout()方法内部又会走到scheduleTraversals()方法。最后会走到performTraversals()方法,接着到了我们熟知的测量、布局、绘制三大流程了。

当我们使用 ValueAnimator.start()、View.invalidate()时,最后也是走到ViewRootImpl的 scheduleTraversals()方法。(View.invalidate()内部会循环获取ViewParent直到ViewRootImpl的invalidateChildInParent()方法,然后走到scheduleTraversals(),可自行查看源码)

即所有UI的变化都是走到ViewRootImpl的scheduleTraversals()方法。

这里注意一个点:scheduleTraversals()之后不是立即就执行performTraversals()的,它们中间隔了一个Choreographer机制。简单来说就是scheduleTraversals()中,Choreographer会去请求native的VSync信号,VSync信号来了之后才会去调用performTraversals()方法进行View绘制的三大流程。

//ViewRootImpl.java

void scheduleTraversals() {

if (!mTraversalScheduled) {

mTraversalScheduled = true;

//添加同步屏障,屏蔽同步消息,保证VSync到来立即执行绘制

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

//mTraversalRunnable是TraversalRunnable实例,最终走到run(),也即doTraversal();

mChoreographer.postCallback(

Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

if (!mUnbufferedInputDispatch) {

scheduleConsumeBatchedInput();

}

notifyRendererOfFramePending();

pokeDrawLockIfNeeded();

}

}

final class TraversalRunnable implements Runnable {

@Override

public void run() {

doTraversal();

}

}

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

void doTraversal() {

if (mTraversalScheduled) {

mTraversalScheduled = false;

//移除同步屏障

mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); …

//开始三大绘制流程

performTraversals();

}

}

  1. postSyncBarrier 开启同步屏障,保证VSync到来后立即执行绘制

  2. mChoreographer.postCallback()方法,发送一个会在下一帧执行的回调,即在下一个VSync到来时会执行 TraversalRunnable–>doTraversal()—>performTraversals()–>绘制流程。

Choreographer

初始化

mChoreographer,是在ViewRootImpl的构造方法内使用 Choreographer.getInstance()创建。

Choreographer和Looper一样是线程单例的,通过ThreadLocal机制来保证唯一性。因为Choreographer内部通过FrameHandler来发送消息,所以初始化的时候会先判断当前线程有无Looper,没有的话直接抛异常。

public static Choreographer getInstance() {

return sThreadInstance.get();

}

private static final ThreadLocal sThreadInstance =

new ThreadLocal() {

@Override

protected Choreographer initialValue() {

Looper looper = Looper.myLooper();

if (looper == null) {

//当前线程要有looper,Choreographer实例需要传入

throw new IllegalStateException(“The current thread must have a looper!”);

}

Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);

if (looper == Looper.getMainLooper()) {

mMainInstance = choreographer;

}

return choreographer;

}

};

postCallback

mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)方法,第一个参数是CALLBACK_TRAVERSAL,表示回调任务的类型,共有以下5种类型:

//输入事件,首先执行

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;

复制代码

五种类型任务对应存入对应的CallbackQueue中,每当收到 VSYNC 信号时,Choreographer 将首先处理 INPUT 类型的任 务,然后是 ANIMATION 类型,最后才是 TRAVERSAL 类型。

postCallback()内部调用postCallbackDelayed(),接着又调用postCallbackDelayedInternal(),正常消息执行scheduleFrameLocked,延迟运行的消息会发送一个MSG_DO_SCHEDULE_CALLBACK类型的meessage:

private void postCallbackDelayedInternal(int callbackType,

Object action, Object token, long delayMillis) {

synchronized (mLock) {

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

if (dueTime <= now) { //立即执行

scheduleFrameLocked(now);

} else {

//延迟运行,最终也会走到scheduleFrameLocked()

Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);

msg.arg1 = callbackType;

msg.setAsynchronous(true);

mHandler.sendMessageAtTime(msg, dueTime);

}

}

}

FrameHandler这个类是内部专门用来处理消息的,可以看到延迟的MSG_DO_SCHEDULE_CALLBACK类型消息最终也是走到scheduleFrameLocked:

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,即绘制过程

doFrame(System.nanoTime(), 0);

break;

case MSG_DO_SCHEDULE_VSYNC:

//申请VSYNC信号,例如当前需要绘制任务时

doScheduleVsync();

break;

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

}

}

}

}

申请VSync信号

scheduleFrameLocked()方法里面就会去真正的申请 VSync 信号了。

private void scheduleFrameLocked(long now) {

if (!mFrameScheduled) {

mFrameScheduled = true;

if (USE_VSYNC) {

//当前执行的线程,是否是mLooper所在线程

if (isRunningOnLooperThreadLocked()) {

//申请 VSYNC 信号

scheduleVsyncLocked();

} else {

// 若不在,就用mHandler发送消息到原线程,最后还是调用scheduleVsyncLocked方法

Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);

msg.setAsynchronous(true);//异步

mHandler.sendMessageAtFrontOfQueue(msg);

}

} else {

// 如果未开启VSYNC则直接doFrame方法(4.1后默认开启)

final long nextFrameTime = Math.max(

mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);

Message msg = mHandler.obtainMessage(MSG_DO_FRAME);

msg.setAsynchronous(true);//异步

mHandler.sendMessageAtTime(msg, nextFrameTime);

}

}

}

VSync信号的注册和监听是通过mDisplayEventReceiver实现的。mDisplayEventReceiver是在Choreographer的构造方法中创建的,是FrameDisplayEventReceiver的实例。 FrameDisplayEventReceiver是 DisplayEventReceiver 的子类,

private void scheduleVsyncLocked() {

mDisplayEventReceiver.scheduleVsync();

}

public DisplayEventReceiver(Looper looper, int vsyncSource) {

if (looper == null) {

throw new IllegalArgumentException(“looper must not be null”);

}

mMessageQueue = looper.getQueue();

// 注册native的VSYNC信号监听者

mReceiverPtr = nativeInit(new WeakReference(this), mMessageQueue,vsyncSource);

mCloseGuard.open(“dispose”);

}

VSync信号回调

native的VSync信号到来时,会走到onVsync()回调:

private final class FrameDisplayEventReceiver extends DisplayEventReceiver

implements Runnable {

@Override

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

//将本身作为runnable传入msg, 发消息后 会走run(),即doFrame(),也是异步消息

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

}

}

最后:学习总结——Android框架体系架构知识脑图(纯手绘xmind文档)

学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。

下方即为我手绘的Android框架体系架构知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的Android框架体系架构知识脑图原件(包括上方的面试解析xmind文档)

除此之外,前文所提及的Alibaba珍藏版 Android框架体系架构 手写文档以及一本 《大话数据结构》 书籍等等相关的学习笔记文档,也皆可分享给认可的朋友!

——感谢大家伙的认可支持,请注意:点赞+点赞+点赞!!!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
档)

学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。

下方即为我手绘的Android框架体系架构知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的Android框架体系架构知识脑图原件(包括上方的面试解析xmind文档)
[外链图片转存中…(img-4Axi6eAn-1714947784319)]

除此之外,前文所提及的Alibaba珍藏版 Android框架体系架构 手写文档以及一本 《大话数据结构》 书籍等等相关的学习笔记文档,也皆可分享给认可的朋友!

——感谢大家伙的认可支持,请注意:点赞+点赞+点赞!!!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值