从上一篇文章:从setContentView揭开DecorView 中可以看到,Activity
是怎么将我们的布局加载到DecorView
,但是这时还不是可见的,因为这时布局还没有绘制。那么它是怎么绘制的,我们来研究一下。
本文源码基于android 27
虽然DecorView
已经被创建出来了,但是目前DecorView
跟PhoneWindow
是没有任何关系的。那么DecorView
是怎么添加到PhoneWindow
上面的呢。这个操作实际上在Activity
启动时就完成了,关于Activity
的启动可以看下这篇文章:Activity启动过程详解。我们直接来看相关代码,在ActivityThread
的handleResumeActivity()
会将DecorView
添加到PhoneWindow
上面:
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
//...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();//获得当前Activity的PhoneWindow对象
View decor = r.window.getDecorView();//获得当前phoneWindow内部类DecorView对象
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();//得当当前Activity的WindowManagerImpl对象
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);//将DecorView添加到当前Activity的窗口上面
}
//...
}
WindowManager
是个抽象类,其具体实现为WindowManagerImpl
,我们直接看WindowManagerImpl
中的addView()
方法:
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);//调用WindowManagerGlobal的addView方法
}
这里的mGlobal
是WindowManagerGlobal
类型,我们继续往下看
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
//...
ViewRootImpl root;
View panelParentView = null;
//...
root = new ViewRootImpl(view.getContext(), display);//创建一个ViewRootImpl对象
//...
root.setView(view, wparams, panelParentView);//将DecorView到ViewRootImpl中
//...
}
我们再来看看ViewRootImpl
的setView()
:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
//...
requestLayout();//请求布局
//...
}
继续往下看:
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();//①
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);//②
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();//③
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();//④
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
通过一系列的调用,最终会走到performTraversals()
这个方法:
private void performTraversals() {
//...
//获得view宽高的测量规格,mWidth和mHeight表示窗口的宽高,lp.widthhe和lp.height表示DecorView根布局宽和高
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//执行测量
//...
performLayout(lp, mWidth, mHeight);//执行布局
//...
performDraw();//执行绘制
//...
}
performTraversals()
会执行View绘制的三大流程:测量,布局,绘制。好了,先到此为止吧。
最后,上张时序图:
measure
、layout
、draw
这三大流程先不分析,下文再来分析。
从上面的分析可以看到,Activity启动时会创建一个ViewRootImpl
对象,这个ViewRootImpl
非常重要,WindowManager
通过ViewRootImpl
与DecorView
起联系。并且,View
的绘制流程都是由ViewRootImpl
发起的。