【面试专题】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);

}

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结

找工作是个很辛苦的事情,而且一般周期都比较长,有时候既看个人技术,也看运气。第一次找工作,最后的结果虽然不尽如人意,不过收获远比offer大。接下来就是针对自己的不足,好好努力了。

最后为了节约大家的时间,我把我学习所用的资料和面试遇到的问题和答案都整理成了PDF文档

喜欢文章的话请关注、点赞、转发 谢谢!

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

知识点,真正体系化!**

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结

找工作是个很辛苦的事情,而且一般周期都比较长,有时候既看个人技术,也看运气。第一次找工作,最后的结果虽然不尽如人意,不过收获远比offer大。接下来就是针对自己的不足,好好努力了。

最后为了节约大家的时间,我把我学习所用的资料和面试遇到的问题和答案都整理成了PDF文档

喜欢文章的话请关注、点赞、转发 谢谢!

[外链图片转存中…(img-Uru0nV6t-1713441955248)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值