前言
一直都有阅读学习源码的习惯,但是没从来没有想过系统的对其进行一个梳理,每次只是看过了以后就过去了,然后过一段时间,就又会遗忘了,所以打算开始慢慢养成将其源码分析写出来,这样一个可以加深对源码的原理理解,另一个也方便以后进行一个回顾。
这里全文是在Android SDK 29的源码上进行分析理解的,众所周知,Android View的绘制流程有三大步骤 OnMeasure(对控件进行测量设置其宽高),OnLayout(根据对控件测量出来的宽高进行屏幕位置布局摆放),OnDraw(将控件内容绘制在屏幕之上),下面就带大家一步一步从源码分析:
ViewRootImpl 绘制入口分析
入口 — ActivityThread#handleResumeActivity,以下都是一些伪代码
class ActivityThread
{
//绘制三大步骤的流程入口
@Overrrid
public void handleResumeActivity(IBiner token,boolean finalStateRquest ,boolean isForward,boolean reason)
{
...
//这里将会执行到Activity的onResume()方法
final ActivityClientRecord r = performResumeActivity(token,finalStateRequest,reason);
....
if (r.window == null && !a.mFinished && willBeVisible)
{
//获取当前Activity关联的Window窗口类,其真正的对象是一个PhoneWindow
r.window = r.activity.getWindow();
//然后获取当前Window的顶级父容器DecorView,这里的DecorView是一个FrameLayout
View decor = r.window.getDecorView();
//会先设置DecorView不可见,目前的绘制流程还未开始
decor.setVisibility(View.INVISIBLE);
//获取ViewManager类型的实例 它只是一个接口,定义了addView,removeView的一些行为
//那么真的对象是哪一个呢?
//答:Activity#getWindowManager()
// ---> mWindow.getWindowManager(); --Activity#attach()方法中
// ---> ((WindowManagerImpl)wm).createLocalWindowManager(this); --- Window#setWindowManager方法中
// ---> new WindowManagerImpl(mContext, parentWindow) ; --- WindowManagerImpl#createLocalWindowManager方法中
//故 这里的实际对象是WindowManagerImpl对象 ,感兴趣的可以按照此流程去查阅源码
ViewManager wm = a.getWindowManager();
//获取当前Window的LayoutParmas布局参数(该布局参数中包含了当前窗口的宽,高等属性)
WindowManager.LayoutParams l = r.window.getAttributes();
....
if (a.mVisibleFromClient)
{
if (!a.mWindowAdded)
{
//设置该属性,表示当前窗口已经附加到Activity之上了
a.mWindowAdded = true;
//将界面顶级布局容器DecoreView 和 Window 的布局参数传递给WindowManager进行添加布局
//这里就是的View的绘制流程开始
//从前文得知 wm 的真正类型是WindowManagerImpl,所以wm.addView将会执行到 WindowManagerImpl#addView方法中
//而WindowManagerImpl#addView --> WindowManagerGlobal#addView
// ---> ViewRootImpl#setView
wm.addView(decor, l);
}
}
}
}
}
从上面源码分析可得知真正绘制流程逻辑是发生在WindowManagerGlobal#addView方法中的,然后在该方法中会创建一个ViewRootImpl对象,该对象是连接WindowManager和DecorView的纽带,View的三大流程就是通过ViewRootImpl来完成的。
总结一下,在ActivityThread中,Activity对象被创建完之后,DecorView会和Window进行绑定,然后通过ManagerManager这个中介去调用WindowManagerGlobal 来创建ViewRootImpl,自此ViewRootImpl就会和DecorView建立了关联,同时ViewRootImpl
对象也会被保存到WindowManagerGlobal对象中
public class RootViewImpl implements ViewParent
{
/**
*
* @params view Decorview
* @parmas attrs Window的布局参数
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView)
{
synchronized (this)
{
if (mView == null)
{
//持有当前传过来的DecorView
mView = view;
...
//这里最终会执行到View的OnMeasure,OnLayout,OnDraw
requestLayout();
...
//ViewRootImpl 与 DecorView 的绑定
//该方法参数ViewParent 而ViewRootImpl正是实现了ViewParent接口的,所以这里就将DecorView和ViewRootImpl
//进行了绑定。正如我们都知道每个Activity的根布局都是DecorView,也就是说实际上View的刷新都是由ViewRootImpl
//来控制的。即使是界面上一个小小的View发起了重回请求,都要层层走到ViewRootImpl,然后由它发起重绘请求
//然后再有它来开始遍历View树,一直遍历到这个需要重绘的View再调用它的onDraw方法进行绘制
view.assignParent(this);
...
}
}
}
总结一下,再打开一个Activity后,当它的onCreate-onStart-onReumse走完了以后,才将它的DecorView与一个新建的ViewRootImpl 对象进行绑定,同时开始安排一次遍历View的任务 也就是View树的操作等待执行,然后再讲DecorView的parent
设置成ViewRootImpl对象,所以这就是为什么在onCreate-onStart-onResume里获取不到View的宽高原因,因为在这个时刻ViewRootImpl甚至都还没开始创建,更不用说是否已经执行过测量操作
public class RootViewImpl implements ViewParent
{
@Override
public void requestLayout()
{
if (!mHandlingLayoutInLayoutRequest)
{
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void scheduleTraversals()
{
if (!mTraversalScheduled)
{
//这里是为了过滤一帧内重复的刷新请求
mTraversalScheduled = true;
//这里发送一个同步屏障消息,防止app接收到屏幕刷新信号时,来不及第一时间就去执行刷新屏幕的操作,
//等到下一帧刷新信号又到来时,就有可能造成丢帧的情况,所以这里采用发送同步屏障消息来防止掉帧
//具体的实现原理(较为复杂) 后面可能会写一篇关于这里消息处理逻辑
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//这里将执行 doTraversal逻辑 包装成一个Runnable 等待 onVsync 屏幕刷新信号回调执行
//这个具体实现原理(较为复杂),同上可能会一起写一篇原理逻辑,所以这里就不做过的解释
//这里就可以直接理解成当底层发出VSync信号后,doTraversals就会被回调执行
mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal()
{
if (mTraversalScheduled)
{
//这里执行的都是和 scheduleTraversals的 相反的操作
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
...
//执行刷新逻辑(onMeasure,onLayout,onDraw)
performTraversals();
...
}
}
private void perf