赞
踩
一直都有阅读学习源码的习惯,但是没从来没有想过系统的对其进行一个梳理,每次只是看过了以后就过去了,然后过一段时间,就又会遗忘了,所以打算开始慢慢养成将其源码分析写出来,这样一个可以加深对源码的原理理解,另一个也方便以后进行一个回顾。
这里全文是在Android SDK 29的源码上进行分析理解的,众所周知,Android View的绘制流程有三大步骤 OnMeasure(对控件进行测量设置其宽高),OnLayout(根据对控件测量出来的宽高进行屏幕位置布局摆放),OnDraw(将控件内容绘制在屏幕之上),下面就带大家一步一步从源码分析:
入口 — 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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。