View的绘制流程分析
1、首先从Activty 的生命周期开始setContentVie()开始。在activity的setContentView 方法里面如:
public void setContentView(@LayoutRes int layoutResID) { getWindow().setContentView(layoutResID); initWindowDecorActionBar(); }
调用的是phoneWindow.setContentView(@LayoutRes int layoutResID) 方法:
@Overridepublic void setContentView(int layoutResID) { // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window // decor, when theme attributes and the like are crystalized. Do not check the feature // before this happens. //一开始mContactParent 是为null的,因此会先初始化DecorVIew if (mContentParent == null) { installDecor(); } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) { mContentParent.removeAllViews(); } .... if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID, getContext()); transitionTo(newScene); } else { //将layout 解析并add 到mContentParent中 mLayoutInflater.inflate(layoutResID, mContentParent); }ontentParent.addView(view, params); } ...
主要关注installDecor(),
private void installDecor() { if (mDecor == null) { // new 一个Decorview 的对象 mDecor = generateDecor(-1); //1 mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); mDecor.setIsRootNamespace(true); if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) { mDecor.postOnAnimation(mInvalidatePanelMenuRunnable); } ..... if (mContentParent == null) { mContentParent = generateLayout(mDecor); //②
首先会先执行会上面的方法1, new 了一个Decorview的对象,而DecorView则是PhoneWindow类的一个内部类,继承于FrameLayout,由此可知它也是一个ViewGroup,那么DecorView起到什么作用呢?
其实DecorView 是viewTree里的顶层View,是继承FrameLayout 的,有标题view和内容view这两个子元素,而内容view则是上面提到的mContentParent。我们接着看②号代码获取对应的mConentParent对应的View .protected ViewGroup generateLayout(DecorView decor) { ... ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT); ...
其中ID_ANDROID_CONTENT = com.android.internal.R.id.content; 对应我们的内容view 中,这个view 就是用来填充我们之前setContantView 方法传过来的layout 对应的view.
现在会回到之前的phoneWindow.setContentView() 在installDecor 后会通过LayoutInflate.infalte 将传过来的layout解析成view 并添加到mContentParent 中。
2、接下来到Activity 的onResume()阶段。
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) { ActivityClientRecord r = mActivities.get(token); ... if (a.mVisibleFromClient) { if (!a.mWindowAdded) { a.mWindowAdded = true; wm.addView(decor, l); ...
在这个阶段会通过windowManager 将onCreate 创建的DecorView add 进去 。WindouwManager 的实现类是WindowManagerImpl如:
@Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) { applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); }
接下来调用到WindowManagerGlobal.addView 方法:
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) { if (view == null) { throw new IllegalArgumentException("view must not be null"); .... root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); ... try { root.setView(view, wparams, panelParentView); } ...
在这里构造了ViewRootImpl类 .继续看root.setView 方法。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) { synchronized (this) { if (mView == null) { mView = view; mAttachInfo.mDisplayState = mDisplay.getState(); mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); .... // Schedule the first layout -before- adding to the window // manager, to make sure we do the relayout before receiving // any other events from the system. requestLayout(); if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) { mInputChannel = new InputChannel(); } ....
接下来来调用了requestLaout 方法
@Override public void requestLayout() { if (!mHandlingLayoutInLayoutRequest) { if (ViewDebugManager.DEBUG_REQUESTLAYOUT) { Log.d(mTag, "requestLayout: mView = " + mView + ", this = " + this, new Throwable("requestLayout")); } checkThread(); mLayoutRequested = true; scheduleTraversals(); } }
在scheduleTraversals 里面将CALLBACK_TRAVERSAL 消息push (mChoreographer.postCallback) 到一个Choreographer的一个队列里面。
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier(); if (ViewDebugManager.DEBUG_SCHEDULETRAVERSALS) { Log.v(mTag, "scheduleTraversals: mTraversalBarrier = " + mTraversalBarrier + ",this = " + this, new Throwable("scheduleTraversals")); } mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); if (!mUnbufferedInputDispatch) { scheduleConsumeBatchedInput(); } notifyRendererOfFramePending(); pokeDrawLockIfNeeded(); } }
在Choreographer 在将CALLBACK_TRAVERSAL 这种类型的数据放到队列的同时会通知Choreographer 的FrameDisplayEventReceiver 接受surfaceflinger 上传的Vsync 信号,收到后执行doFrame方法,最后会执行到TraversalRunnable 的run()方法,执行doTraversal 操作。
final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } }
void doTraversal() { if (ViewDebugManager.DEBUG_LIFECYCLE || ViewDebugManager.DEBUG_ENG) { Log.v(mTag, "doTraversal: mTraversalScheduled = " + mTraversalScheduled + " mFisrt = " + mFirst + ",mTraversalBarrier = " + mTraversalBarrier + ",this = " + this); } if (mTraversalScheduled) { mTraversalScheduled = false; mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier); if (mProfile) { Debug.startMethodTracing("ViewAncestor"); } performTraversals(); if (mProfile) { Debug.stopMethodTracing(); mProfile = false; } } }
doTraversal 主要调用了performTraversals()方法,在该方法里面分布调用performMeasure,performLayout,performDraw 分布完成view 的测量,布局,和绘制操作。
private void performTraversals() { // cache mView since it is used so much below... final View host = mView; ... performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); ... performLayout(lp, mWidth, mHeight); ... performDraw(); ...
到了上面的流程后,就是对于的view 的测量,布局,和绘制操作流程分析了。 下一节分析ViewGroup的绘制流程