在Activity布局加载流程源码解析一文中,我们分析了Activity布局加载流程,通过分析我们了解到Activity通过Window来控制界面的展示,一个Activity包含一个Window对象(具体由PhoneWindow来实现),并且PhoneWindow将DecorView作为整个应用窗口的根View。当时,我们在分析的时候遗留了一个问题,那就是DecorView是什么时候添加到Window上去的?那么,这篇文章就来具体介绍一下Activity布局绘制流程的源码分析,而且源码版本基于Android 8.0。
一、从ActivityThread的handleResumeActivity方法说起
在Activity的onCreate()方法中,我们通过setContentView()方法完成了xml布局解析以及DecorView的创建。之后,DecorView会被添加到Window上去,这个过程的起始点在ActivityThread的handleResumeActivity方法中。
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
return;
}
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;
// TODO Push resumeArgs into the activity for consideration
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
... ...
// The window is now visible if it has been added, we are not
// simply finishing, and we are not starting another activity.
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
... ...
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
... ...
} else {
// If an exception was thrown when trying to resume, then
// just end this activity.
try {
ActivityManager.getService()
.finishActivity(token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
该方法的实现代码比较多,我们只关注重点部分。由源码可知,在获取了Activity的Window相关参数之后,执行了r.activity.makeVisible()方法,即接下来回去执行Activity的makeVisible()方法,这个方法就是DecorView被添加到Window上去的起点。
二、Activity中的makeVisible方法
makeVisible字面上的意思是让它变为可见,我们看一下它的具体实现:
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
由源码可知,这里的mWindowAdded是一个布尔类型的成员变量,很明显这里的主要逻辑if分支只会执行一次,因为执行过后mWindowAdded就会被赋值为true,之后便再也不会执行if分支代码。紧接着,通过getWindowManager()方法获取ViewManager对象,我们看一下具体实现。
// Activity # getWindowManager()
/** Retrieve the window manager for showing custom windows. */
public WindowManager getWindowManager() {
return mWindowManager;
}
从方法的注释来看是返回一个Window管理器,并且是以成员变量的形式返回。那么,很自然我们就要去寻找它是在哪边被赋值的,通过代码检索,我们很容易发现它是在Activity的attch()方法中被赋值的。
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
... ...
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
mWindow.setColorMode(info.colorMode);
}
这边我稍微精简了一下代码,我们可以看到mWindowManager是通过PhoneWindow的getWindowManager()方法返回的。但是,通过查看源码,我们可以发现PhoneWindow继承于Window,而且没有重写getWindowManager()方法,所以最终还是会调用到Window的getWindowManager()方法。
// Window # getWindowManager()
/**
* Return the window manager allowing this Window to display its own
* windows.
*
* @return WindowManager The ViewManager.
*/
public WindowManager getWindowManager() {
return mWindowManager;
}
好吧,Window的getWindowManager()方法同样是返回它的成员变量,所以我们依旧需要去找它是在哪边被赋值的。这个还是比较好找的,最终我们可以发现它是在setWindowManager()方法中被赋值的。
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
由源码可知,mWindowManager最终是通过WindowManagerImpl的createLocalWindowManager()方法创建的。
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
可以看到,这个方法很简单,就是通过new的方式创建了一个WindowManagerImpl对象并返回。兜了这么一大圈,我们终于知道Activity的makeVisible()方法中getWindowManager()返回的其实就是一个WindowManagerImpl对象。刚好,我们顺便理一下继承关系,就是WindowManage继承自ViewManager,而WindowManagerImpl又继承自WindowManage。
接下来,我们继续分析Activity的makeVisible()方法,在通过getWindowManager()方法获取到WindowManagerImpl对象之后,接着执行了如下方法:
wm.addView(mDecor, getWindow().getAttributes());
通过以上分析,我们很容易得知,这其实就是去调用WindowManagerImpl的addView方法,并且把DecorView作为入参。
// WindowManagerImpl # addView()
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
由源码可知,mGlobal是一个WindowManagerGlobal的单例对象,是Window处理的工具类,用于操作View组件。我们具体看一下mGlobal的addView()方法的实现:
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
... ...
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
... ...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
... ...
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
由于代码实现较长,稍微做了一下精简,我们只关注重点部分。首先说一下上述代码中的3个ArrayList,其中mViews主要用于保存Activity的DecorView(Activity的根View),mRoots主要用于保存ViewRootImpl,mParams主要用于保存Window的LayoutParams。最后,调用了ViewRootImpl的setView()方法,这个方法就很关键了,将在下面进行具体分析。
三、ViewRootImpl的setView()方法
在ViewRootImpl的setView()方法,会完成对View的测量、布局和绘制。并且,通过Binder跨进程的方式向WMS(WindowManagerService)发起一个调用,从而将DecorView最终添加到Window上。在这个过程中,ViewRootImpl、DecorView和WMS会彼此向相互关联。
我们先来看一下View的测量、布局和绘制过程,在ViewRootImpl的setView()方法中,会调用requestLayout()方法。
// ViewRootImpl # setView()
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout();
...
}
下面我们看下requestLayout()方法的具体实现:
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
我们可以看到,这里首先回去调用checkThread()方法,主要检查当前线程是否为UI线程,若当前线程非UI线程,则抛出非UI线程更新UI的错误。其中,mThread成员变量是在ViewRootImpl的构造方法中被赋值的,而ViewRootImpl的构造方法又是在UI线程中被调用的,所以mThread自然也就被赋值为UI线程。在完成UI线程检查之后,接着会去调用scheduleTraversals()方法,我们看下它的具体实现:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
由源码可知,mChoreographer.postCallback,内部会调用一个异步消息,用于执行mTraversalRunnable的run()方法,我们来具体看一下mTraversalRunnable的具体定义:
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
很明显,最后后调用到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;
}
}
}
我们可以看到,doTraversal()方法内部又会去调用performTraversals()方法,其实这个方法就是整个View的绘制流程的起始方法,我们一起来看下,这个方法非常非常的长,我们只关注重点部分的代码。
private void performTraversals() {
... ...
// View测量
if (!mStopped || mReportNextDraw) {
boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
(relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
|| mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// Implementation of weights from WindowManager.LayoutParams
// We just grow the dimensions as needed and re-measure if
// needs be
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
if (lp.horizontalWeight > 0.0f) {
width += (int) ((mWidth - width) * lp.horizontalWeight);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (lp.verticalWeight > 0.0f) {
height += (int) ((mHeight - height) * lp.verticalWeight);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (measureAgain) {
if (DEBUG_LAYOUT) Log.v(TAG,
"And hey let's measure once more: width=" + width
+ " height=" + height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
layoutRequested = true;
}
}
}
... ...
// View布局
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
// By this point all views have been sized and positioned
// We can compute the transparent area
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
// start out transparent
// TODO: AVOID THAT CALL BY CACHING THE RESULT?
host.getLocationInWindow(mTmpLocation);
mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + host.mRight - host.mLeft,
mTmpLocation[1] + host.mBottom - host.mTop);
host.gatherTransparentRegion(mTransparentRegion);
if (mTranslator != null) {
mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
}
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
// reconfigure window manager
try {
mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
} catch (RemoteException e) {
}
}
}
}
... ...
// View绘制
if (!cancelDraw && !newSurface) {
if (!skipDraw || mReportNextDraw) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
performDraw();
}
} else {
if (viewVisibility == View.VISIBLE) {
// Try again
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}
mIsInTraversal = false;
}
由源码可知,在performTraversals()方法中,主要是依次调用了performMeasure(),performLayout(),performDraw()这三个方法,用于完成View的测量、布局和绘制。
View的测量
我们一起先来看下performMeasure()方法的具体实现。
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
我们看到,在该方法中调用了mView的measure()方法,通过源码追踪我们可以很容易发现这个成员变量mView其实就是传递进来的DecorView对象,所以最终调用的是DecorView的measure()方法,我们具体看下。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
... ...
if (forceLayout || needsLayout) {
... ...
int cacheIndex = forceLayout ? -1 : mMeasureCache.indexOfKey(key);
if (cacheIndex < 0 || sIgnoreMeasureCache) {
// measure ourselves, this should set the measured dimension flag back
onMeasure(widthMeasureSpec, heightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
} else {
long value = mMeasureCache.valueAt(cacheIndex);
// Casting a long to int drops the high 32 bits, no mask needed
setMeasuredDimensionRaw((int) (value >> 32), (int) value);
mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;
}
... ...
}
我们可以看到,measure方法主要是做一些判断,如果View需要测量就会去调用它的onMeasure()方法。下面,我们就来看一下DecorView的onMeasure()方法。
// DecorView # onMeasure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int widthMode = getMode(widthMeasureSpec);
final int heightMode = getMode(heightMeasureSpec);
... ...
boolean measure = false;
... ...
if (!fixedWidth && widthMode == AT_MOST) {
... ...
if (width < min) {
widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
measure = true;
}
}
}
if (measure) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
我们可以看到,DecorView的onMeasure()方法主要是用来判断DecorView需不需要测量,如果需要测量,则调用其父类FrameLayout的onMeasure()方法。
// FrameLayout # onMeasure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
... ...
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec;
... ...
final int childHeightMeasureSpec;
... ...
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
由源码可知,这里主要通过for循环,获取该View的所有子View,并执行所有子View的measure方法,如果子View是ViewGroup就会调用ViewGroup的onMeasure()方法,若果子View是View就会调用View的onMeasure()方法。因此,我们还需要看一下View的onMeasure()方法。
// View # onMeasure()
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
boolean optical = isLayoutModeOptical(this);
if (optical != isLayoutModeOptical(mParent)) {
Insets insets = getOpticalInsets();
int opticalWidth = insets.left + insets.right;
int opticalHeight = insets.top + insets.bottom;
measuredWidth += optical ? opticalWidth : -opticalWidth;
measuredHeight += optical ? opticalHeight : -opticalHeight;
}
setMeasuredDimensionRaw(measuredWidth, measuredHeight);
}
private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight) {
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
这样,通过一系列的方法调用就可以把View即其子View的大小测量出来了,并且保存在了成员变量mMeasuredWith和mMeasuredHeight中。
View的布局
回到performTraversals()方法中,我们继续看performLayout()方法。
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
... ...
final View host = mView;
if (host == null) {
return;
}
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
... ...
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
我们可以看到,该方法中主要调用了host的layout()方法,而host又是通过mView(DecorView)赋值的,所以就是去调用DecorView的layout()方法。通过源码可以发现最终调用的是ViewGroup的layout()方法。
// ViewGroup # layout()
@Override
public final void layout(int l, int t, int r, int b) {
if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
if (mTransition != null) {
mTransition.layoutChange(this);
}
super.layout(l, t, r, b);
} else {
// record the fact that we noop'd it; request layout when transition finishes
mLayoutCalledWhileSuppressed = true;
}
}
好吧,这里又调用了其父类的layout()方法,所以又会去调用View的layout()方法。
public void layout(int l, int t, int r, int b) {
... ...
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
... ...
}
}
我们可以看到,layout()方法主要还是做一些判断,如果View有变化需要重新布局,则会去调用它的onLayout()方法。所以,接下来我们需要看一下DecorView的onLayout()方法。
// DecorView # onLayout()
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
getOutsets(mOutsets);
if (mOutsets.left > 0) {
offsetLeftAndRight(-mOutsets.left);
}
if (mOutsets.top > 0) {
offsetTopAndBottom(-mOutsets.top);
}
if (mApplyFloatingVerticalInsets) {
offsetTopAndBottom(mFloatingInsets.top);
}
if (mApplyFloatingHorizontalInsets) {
offsetLeftAndRight(mFloatingInsets.left);
}
// If the application changed its SystemUI metrics, we might also have to adapt
// our shadow elevation.
updateElevation();
mAllowUpdateElevation = true;
if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) {
getViewRootImpl().requestInvalidateRootRenderNode();
}
}
我们看到这个方法中又会去调用其父类Framelayout的onLayout()方法。
// Framelayout # onLayout()
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
... ...
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
Framelayout的onLayout()方法又会去调用layoutChildren()方法,具体代码上面已经贴出来了。与measure类似,这里也是通过for循环获取该View的所有子View,如果子View是ViewGroup则执行ViewGroup的layout()方法,如果子View是View则执行View的layout()方法。
这样,通过一系列的方法调用就可以把View及其子View的位置信息计算出来并保存在成员变量中。
View的绘制
好了,我们再次回到performTraversals()方法中,继续看performDraw()方法。
private void performDraw() {
... ...
draw(fullRedrawNeeded);
... ...
}
private void draw(boolean fullRedrawNeeded) {
... ...
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
... ...
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty) {
... ...
mView.draw(canvas);
... ...
return true;
}
对源码做了大量的精简,我们看到最后会辗转调用到mView的draw()方法,也就是调用DecorView的draw()方法。
// DecorView # draw()
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (mMenuBackground != null) {
mMenuBackground.draw(canvas);
}
}
DecorView的draw()方法又会去调用其父类FrameLayout的draw()方法,最终又会调用到View的draw()方法,一起来看下。
// View # draw()
@CallSuper
public void draw(Canvas canvas) {
final int privateFlags = mPrivateFlags;
final boolean dirtyOpaque = (privateFlags & PFLAG_DIRTY_MASK) == PFLAG_DIRTY_OPAQUE &&
(mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;
/*
* Draw traversal performs several drawing steps which must be executed
* in the appropriate order:
*
* 1. Draw the background
* 2. If necessary, save the canvas' layers to prepare for fading
* 3. Draw view's content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)
*/
// Step 1, draw the background, if needed
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
// skip step 2 & 5 if possible (common case)
final int viewFlags = mViewFlags;
boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
if (!verticalEdges && !horizontalEdges) {
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
// Step 7, draw the default focus highlight
drawDefaultFocusHighlight(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
// we're done...
return;
}
/*
* Here we do the full fledged routine...
* (this is an uncommon case where speed matters less,
* this is why we repeat some of the tests that have been
* done above)
*/
boolean drawTop = false;
boolean drawBottom = false;
boolean drawLeft = false;
boolean drawRight = false;
float topFadeStrength = 0.0f;
float bottomFadeStrength = 0.0f;
float leftFadeStrength = 0.0f;
float rightFadeStrength = 0.0f;
// Step 2, save the canvas' layers
int paddingLeft = mPaddingLeft;
final boolean offsetRequired = isPaddingOffsetRequired();
if (offsetRequired) {
paddingLeft += getLeftPaddingOffset();
}
int left = mScrollX + paddingLeft;
int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
int top = mScrollY + getFadeTop(offsetRequired);
int bottom = top + getFadeHeight(offsetRequired);
if (offsetRequired) {
right += getRightPaddingOffset();
bottom += getBottomPaddingOffset();
}
final ScrollabilityCache scrollabilityCache = mScrollCache;
final float fadeHeight = scrollabilityCache.fadingEdgeLength;
int length = (int) fadeHeight;
// clip the fade length if top and bottom fades overlap
// overlapping fades produce odd-looking artifacts
if (verticalEdges && (top + length > bottom - length)) {
length = (bottom - top) / 2;
}
// also clip horizontal fades if necessary
if (horizontalEdges && (left + length > right - length)) {
length = (right - left) / 2;
}
if (verticalEdges) {
topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
drawTop = topFadeStrength * fadeHeight > 1.0f;
bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
drawBottom = bottomFadeStrength * fadeHeight > 1.0f;
}
if (horizontalEdges) {
leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
drawLeft = leftFadeStrength * fadeHeight > 1.0f;
rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
drawRight = rightFadeStrength * fadeHeight > 1.0f;
}
saveCount = canvas.getSaveCount();
int solidColor = getSolidColor();
if (solidColor == 0) {
final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
if (drawTop) {
canvas.saveLayer(left, top, right, top + length, null, flags);
}
if (drawBottom) {
canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
}
if (drawLeft) {
canvas.saveLayer(left, top, left + length, bottom, null, flags);
}
if (drawRight) {
canvas.saveLayer(right - length, top, right, bottom, null, flags);
}
} else {
scrollabilityCache.setFadeColor(solidColor);
}
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Step 5, draw the fade effect and restore layers
final Paint p = scrollabilityCache.paint;
final Matrix matrix = scrollabilityCache.matrix;
final Shader fade = scrollabilityCache.shader;
if (drawTop) {
matrix.setScale(1, fadeHeight * topFadeStrength);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, right, top + length, p);
}
if (drawBottom) {
matrix.setScale(1, fadeHeight * bottomFadeStrength);
matrix.postRotate(180);
matrix.postTranslate(left, bottom);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, bottom - length, right, bottom, p);
}
if (drawLeft) {
matrix.setScale(1, fadeHeight * leftFadeStrength);
matrix.postRotate(-90);
matrix.postTranslate(left, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(left, top, left + length, bottom, p);
}
if (drawRight) {
matrix.setScale(1, fadeHeight * rightFadeStrength);
matrix.postRotate(90);
matrix.postTranslate(right, top);
fade.setLocalMatrix(matrix);
p.setShader(fade);
canvas.drawRect(right - length, top, right, bottom, p);
}
canvas.restoreToCount(saveCount);
drawAutofilledHighlight(canvas);
// Overlay is part of the content and draws beneath Foreground
if (mOverlay != null && !mOverlay.isEmpty()) {
mOverlay.getOverlayView().dispatchDraw(canvas);
}
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
if (debugDraw()) {
debugDrawFocus(canvas);
}
}
我们可以看到,View的整个绘制流程还是比较清楚的,整个执行逻辑一共大概需要六步,并且在执行draw()方法的过程中,如果包含子View,那么也会执行子View的draw()方法。这样,经过一系列的方法调用之后,DectorView及其子View就被绘制出来了。
至此,ViewRootImpl的setView()方法中的requestLayout()部分的分析差不多已经结束了。
四、DecorView添加到Window上
我们回到ViewRootImpl的setView()方法中,requestLayout()方法调用完毕之后,View的测量、布局和绘制已经完成了,下面就是要把DecorView真正添加到Window上去了。
// ViewRootImpl # setView()
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
requestLayout();
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
}
...
}
我们可以看到,通过mWindowSession的addToDisplay()方法调用,将DecorView添加到Window上去。我们先看一下mWindowSession是如何赋值的。
mWindowSession = WindowManagerGlobal.getWindowSession();
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
try {
if (sWindowManagerService != null) {
ValueAnimator.setDurationScale(
sWindowManagerService.getCurrentAnimatorScale());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowManagerService;
}
}
熟悉Binder跨进程通信的同学应该知道,最后应该是通过WindowManagerService的openSession()方法获取到了mWindowSession对象。
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
我们看到openSession()方法通过new的方式返回了一个IWindowSession类型的对象。
回到ViewRootImpl的setView()方法,我们看到它调用了mWindowSession的addToDisplay()方法,这里也就是调用了Session的addToDisplay()方法。
// Session # addToDisplay()
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
由源码可知,这里的成员变量mService是WindowManagerService对象,所以最终会去调用WindowManagerService的addWindow()方法。因此,最终是由WindowManagerService来完成将DecorView添加到Window上。