大厂的Android面试其实并没有大家想象中的困难,很多问题都是换汤不换药的
作者:
偶尔皮一下的Raina
链接:https://juejin.im/post/5e64390bf265da575f4e7de8
本文主要解决以下几个问题:
- 我们都知道Android的刷新频率是60帧/秒,这是不是意味着每隔16ms就会调用一次onDraw方法?
- 如果界面不需要重绘,那么
16ms
到后还会刷新屏幕吗? - 我们调用
invalidate()
之后会马上进行屏幕刷新吗? - 我们说丢帧是因为主线程做了耗时操作,为什么主线程做了耗时操作就会引起丢帧?
- 如果在屏幕快要刷新的时候才去
OnDraw()
绘制,会丢帧吗?
好了,带着以上问题,我们进入源码来找寻答案。
一、屏幕绘制流程
屏幕绘制机制的基本原理可以概括如下:
整个屏幕绘制的基本流程是:
- 应用向系统服务申请buffer
- 系统服务返回buffer
- 应用绘制后提交buffer给系统服务
如果放到Android中来,那么就是:
在Android中,一块Surface对应一块内存,当内存申请成功后,App端才有绘图的地方。由于Android的view绘制不是今天的重点,所以这里点到为止~
二、屏幕刷新分析
屏幕刷新的时机是当Vsync信号到来的时候,具体如图:
在Android端,是谁在控制Vsync
的产生?又是谁来通知我们应用进行刷新的呢?在Android中,Vysnc
信号的产生是由底层HWComposer负责的,而通知应用进行刷新,是Java层的Choreographer
,Android整个屏幕刷新的核心就在于这个Choreographer
。下面我们结合代码一起来看一下。每次当我们要进行ui重绘的时候,都会调用requestLayout(),所以,我们从这个方法入手:
2.1 requestLayout()
----》类名:ViewRootImpl
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
//重点
scheduleTraversals();
}
}
2.2 scheduleTraversals()
----》类名:ViewRootImpl
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
......
}
}
可以看到,在这里并没有立即进行重绘,而是做了两件事情:
- 往消息队列里面插入一条SyncBarrier(同步屏障)
- 通过
Cherographer post
了一个callback
接下来,我们简单说一下这个SyncBarrier(同步屏障)。异步屏障的作用在于:
- 阻止同步消息的执行
- 优先执行异步消息
为什么要设计这个SyncBarrier
呢?主要原因在于,在Android中,有些消息是十分紧急的,需要马上执行,如果说消息队列里面普通消息太多的话,那等到执行