在个人系列文章上一篇:App 居然是这样跑起来的 —— Android App/Activity 启动流程分析中已经分析了一个 App 从点击它的图标到 Activity 的 onCreate()、onStart() 和 onResume() 等生命周期被调用的整个流程。咱们都知道,普通 App 屏幕上显示的内容都是由一个个本身设计的界面被系统加载而来的,而这些界面中的元素又是怎么被渲染出来的呢?本文将继续基于 Android Nougat 从源码的角度来进一步分析整个过程。java
在开始以前,回顾一下上一篇文章中分析的从 ActivityThread 到 Activity 过程的时序图:android
如上图所示,在 Activity 的 onCreate()、onStart() 和 onResume() 等生命周期被调用以前,它的 attach() 方法将会先被调用,所以,咱们将 attach() 方法做为这篇文章主线的开头:canvas
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) {
attachBaseContext(context);
...
// mWindow 是一个 PhoneWindow 对象实例
mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
...
// 调用 Window 的 setWindowManager 方法
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());
}
// 从 Window 中获取 WindowManager
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}
复制代码
mWindow 是一个 Window 类型的变量,在 attach() 方法中,建立了一个 PhoneWindow 对象实例并赋值给了 mWindow,PhoneWindow 直接继承自 Window 类。而后调用了 Window 的 setWindowManager() 方法:app
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) {
...
// mWindowManager 就是 WindowManagerImpl 对象的实例
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
复制代码
所以,Acitvity 中的 mWindow 变量就是 PhoneWindow 类的实例,而 mWindowManager 是 WindowManagerImpl 类的实例,attach() 方法的主要工做就是初始化这两个变量。less
接下来到了 onCreate 方法,咱们都知道,若是想要让本身设计的 layout 布局文件或者 View 显示在 Activity 中,必需要在 Activity 的 onCreate() 方法中应该调用 setContentView() 方法将咱们的布局 id 或者 View 传递过去,查看其中一个 setContentView() 方法:ide
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
复制代码
继续查看 PhoneWindow 类的 setContentView() 方法:oop
public void setContentView(int layoutResID) {
if (mContentParent == null) {// 是否首次调用
// 初始化 Decor
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {// 转场动画,默认 false
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
...
} else {
// 解析布局文件
mLayoutInflater.inflate(layoutResID, mContentParent);
}
...
}
复制代码
若是是首次调用这个方法,则 mContentParent 为 null,不然若是没有转场动画的话就移除 mContentParent 的所有子 View,继续跟踪 installDecor() 方法:源码分析
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
// 生成 DecorView 对象
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
// 调用 generateLayout 方法
mContentParent = generateLayout(mDecor);
...
}
}
}
复制代码
当 mDecor 为 null 的时候会调用 generateDecor() 方法建立一个 DecorView 类的实例,DecorView 继承自 FrameLayout。接下来判断 mContentParent 是否为 null(前面已经提到过,首次加载的时候就是 null),若是是则调用 generateLayout() 方法,这个方法就会建立 mContentParent 对象,跟踪进去:布局
protected ViewGroup generateLayout(DecorView decor) {
TypedArray a = getWindowStyle();
...
// 经过 WindowStyle 中设置的各类属性对 Window 进行各类初始化操做
mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
& (~getForcedWindowFlags());
if (mIsFloating) {
setLayout(WRAP_CONTENT, WRAP_CONTENT);
setFlags(0, flagsToUpdate);
} else {
setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
}
....
int layoutResource;
int features = getLocalFeatures();
// 根据设定好的 features 值获取相应的布局文件并赋值给 layoutResource
if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
layoutResource = R.layout.screen_swipe_dismiss;
} else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleIconsDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_title_icons;
}
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
&& (features & (1 << FEATURE_ACTION_BAR)) == 0) {
layoutResource = R.layout.screen_progress;
} else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogCustomTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else {
layoutResource = R.layout.screen_custom_title;
}
removeFeature(FEATURE_ACTION_BAR);
} else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
if (mIsFloating) {
TypedValue res = new TypedValue();
getContext().getTheme().resolveAttribute(
R.attr.dialogTitleDecorLayout, res, true);
layoutResource = res.resourceId;
} else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
layoutResource = a.getResourceId(
R.styleable.Window_windowActionBarFullscreenDecorLayout,
R.layout.screen_action_bar);
} else {
layoutResource = R.layout.screen_title;
}
} else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
layoutResource = R.layout.screen_simple_overlay_action_mode;
} else {
layoutResource = R.layout.screen_simple;
}
mDecor.startChanging();
// 调用 onResourcesLoaded 方法
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// 在 layoutResource 中根据 id:com.android.internal.R.id.content 获取一个 ViewGroup 并赋值给 contentParent 对象
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
...
mDecor.finishChanging();
// 返回 contentParent
return contentParent;
}
复制代码
DecorView 的 onResourcesLoaded() 方法:post
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
...
mDecorCaptionView = createDecorCaptionView(inflater);
// 解析 layoutResource 文件
final View root = inflater.inflate(layoutResource, null);
if (mDecorCaptionView != null) {
...
} else {
// 做为根布局添加到 mDecor 中
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
复制代码
能够看到,generateLayout 方法主要分为四个部分:
关于第四点,可能有人会有疑问,为何根据 id 为 com.android.internal.R.id.content 就必定能找到对应的 ViewGroup?答案就在前面咱们分析过的 generateLayout() 方法中,这里会根据设定好的 features 值获取相应的布局文件并赋值给 layoutResource,而这全部的布局文件中都包括了一个 id 为 content 的 FrameLayout,除此以外有些布局文件中还可能有 ActiionBar 和 Title 等,这些布局文件存放于该目录下。以 R.layout.screen_simple 为例,它的内容以下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical">
<ViewStub android:id="@+id/action_mode_bar_stub" android:inflatedId="@+id/action_mode_bar" android:layout="@layout/action_mode_bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="?attr/actionBarTheme" />
<FrameLayout android:id="@android:id/content" android:layout_width="match_parent" android:layout_height="match_parent" android:foregroundInsidePadding="false" android:foregroundGravity="fill_horizontal|top" android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
复制代码
回到 PhoneWindow 的 setContentView() 方法,执行完 installDecor() 以后,mDecor 被初始化了,同时 mContentParent 也被赋了值, 回到 setContentView() 方法,最后一个重要步骤就是经过 mLayoutInflater.inflater 将咱们的 layout 布局文件压入 mDecor 中 id 为 content 的 FrameLayout 中。
public void setContentView(int layoutResID) {
if (mContentParent == null) {// 是否首次调用
// 初始化 Decor
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {// 转场动画,默认 false
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
...
} else {
// 解析布局文件
mLayoutInflater.inflate(layoutResID, mContentParent);
}
...
}
复制代码
至此,setContentView() 方法的流程就走完了,整体来看分为三个步骤:
这个过程的时序图以下:
Activity、PhoneWindow、DecorView 和 ContentView 的关系以下图所示:
可是,此时咱们的布局尚未显示出来,接着往下看。
在开篇的时序图中咱们能够看到,ActivityThread 在 handleLaunchActivity() 方法中间接调用了 Activity 的 attach() 和 onCreate():
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
WindowManagerGlobal.initialize();
// 执行 performLaunchActivity 方法
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
Bundle oldState = r.state;
// 执行 handleResumeActivity 方法,最终调用 onStart 和 onResume 方法
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
if (!r.activity.mFinished && r.startsNotResumed) {
r.activity.mCalled = false;
mInstrumentation.callActivityOnPause(r.activity);
r.paused = true;
}
} else {
// 中止该 Activity
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
}
}
复制代码
接着会调用 handleResumeActivity() 方法:
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
...
// 最终会调用 onStart() 和 onResume() 等方法
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
// 获取 DecorView
View decor = r.window.getDecorView();
// 将 DecorView 设置成不可见
decor.setVisibility(View.INVISIBLE);
// 获取 ViewManager,这里是 WindowManagerImpl 实例
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
...
if (a.mVisibleFromClient && !a.mWindowAdded) {
// 标记设置为 true
a.mWindowAdded = true;
// 调用 WindowManagerImpl 的 addView 方法
wm.addView(decor, l);
}
} else if (!willBeVisible) {
...
}
...
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
...
if (r.activity.mVisibleFromClient) {
// 调用 makeVisible 方法将 DecorView 设置为可见
r.activity.makeVisible();
}
}
...
} else {
try {
// 在此过程出现异常,则直接杀死 Activity
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
复制代码
执行完 performResumeActivity() 方法以后,接着会取出 ActivityClientRecord 中的 Activity 对象,并获得以前在 setContentView 流程中初始化好的 DecorView 对象,而后会将它做为参数传入 ViewManager 类型的对象 wm 的 addView 方法,ViewManager 是一个接口,那么它是由谁来实现的呢?这里先打个岔,回头看一下 Activity 的 attach() 方法:
final void attach(...) {
...
// mWindow 是一个 PhoneWindow 对象
mWindow = new PhoneWindow(this, window);
...
// 调用 Window 的 setWindowManager 方法
mWindow.setWindowManager(...);
...
}
复制代码
跟踪 Window 的 setWindowManager() 方法:
public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) {
...
// mWindowManager 就是 WindowManagerImpl 对象的实例
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
复制代码
所以,回到主线,Activity 的 getWindowManager() 获取到的就是 WindowManagerImpl 对象的实例,再看它的 addView() 方法:
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
// mGlobal 是 WindowManagerGlobal 对象的实例
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
复制代码
mGlobal 是 WindowManagerGlobal 对象的实例,查看它的 addView() 方法:
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
...
root = new ViewRootImpl(view.getContext(), display);
...
mRoots.add(root);
...
}
try {
// 将传过来的 DecorView 添加到 ViewRootImpl 中
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
...
}
}
复制代码
DecorView 添加到 ViewRootImpl 以后,便和 ViewRootImpl 创建了联系,再看 ViewRootImpl 的 setView() 方法:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
...
requestLayout();
...
view.assignParent(this);
...
}
}
}
复制代码
跟踪 ViewRootImpl 的 requestLayout() 方法:
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
复制代码
跟踪 ViewRootImpl 的 scheduleTraversals() 方法:
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 设置同步障碍,暂停处理后面的同步消息
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
// 在下一帧到来的时候执行 mTraversalRunnable
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
...
}
}
复制代码
首先,设置同步障碍,暂停处理后面的同步消息,而后利用 Choreographer 类在下一绘制帧来临的时候执行 mTraversalRunnable 对象(关于 Choreographer 原理,可查看Android系统Choreographer机制实现过程)。mTraversalRunnable 是一个 Runnable 对象:
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
复制代码
run() 方法里面只有一句代码,doTraversal() 方法以下:
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 移除同步障碍
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
...
// 正式进入 View 绘制流程
performTraversals();
...
}
}
复制代码
移除了同步障碍以后,全部绘制以前的准备工做已经执行完毕,接下来会调用performTraversals() 方法正式进入 View 的绘制流程。
这里不要忘了,handleResumeActivity() 方法最后还有一句代码还没有执行:
r.activity.makeVisible();
别急,主线流程还没走完呢。
这一步骤主要的工做内容是:
这个过程的时序图以下:
接上一步骤的内容,绘制前的准备工做完成后,ViewRootImpl 的 performTraversals() 方法将会被调用,这个方法内容至关多,忽略条件判断,精简事后以下:
private void performTraversals() {
...
WindowManager.LayoutParams lp = mWindowAttributes;
...
// 获取 DecorView 宽和高的 MeasureSpec
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
...
// 执行 Measure 流程
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
// 执行 Layout 流程
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
// 执行 Draw 流程
performLayout();
...
}
复制代码
首先,根据 getRootMeasureSpec() 方法获取到 childWidthMeasureSpec 和 childHeightMeasureSpec 的值,用于 DecorView 的绘制。由于 DecorView 是全部子元素的根元素,子元素的布局层层嵌套,所以会接着从 DecorView 开始进行一层层地对全部子元素进行测量、布局和绘制,分别对应 performMeasure()、performLayout() 和 performLayout() 方法,整个过程的示意图以下:
MeasureSpec 是 View 的一个内部类,简单来讲就是一个 32 位的 int 值,采用它的高 2 位表示三种 SpecMode(测量模式),低 30 位用来表示 SpecSize(某种测量模式下的规格大小)。采用这种表示方法是为了不建立过多的对象以减小内存分配,MeasureSpec 的定义以下:
public static class MeasureSpec {
private static final int MODE_SHIFT = 30;
private static final int MODE_MASK = 0x3 << MODE_SHIFT;
// 不限定测量模式:父容器不对 View 做任何限制,View 想要多大给多大,
// 这种模式一般用于系统内部。
public static final int UNSPECIFIED = 0 << MODE_SHIFT;
// 精确为 SpecSize:父容器已经肯定 View 须要的大小,就是 SpecSize,
// 对应布局参数是 match_parent 或具体数值时的状况
public static final int EXACTLY = 1 << MODE_SHIFT;
// 最大只能是 SpecSize:父容器规定 View 最大只能是 SpecSize,
// 对应布局参数是 wrap_content 时的状况
public static final int AT_MOST = 2 << MODE_SHIFT;
// 根据 SpecMode 和 SpecSize 建立一个 MeasureSpec
public static int makeMeasureSpec(int size, int mode) {
if (sUseBrokenMakeMeasureSpec) {
return size + mode;
} else {
return (size & ~MODE_MASK) | (mode & MODE_MASK);
}
}
// 获取 SpecMode
public static int getMode(int measureSpec) {
return (measureSpec & MODE_MASK);
}
// 获取 SpecSize
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
}
// 调整 MeasureSpec
static int adjust(int measureSpec, int delta) {
final int mode = getMode(measureSpec);
if (mode == UNSPECIFIED) {
return make MeasureSpec(0, UNSPECIFIED);
}
int size = getSize(measureSpec) + delta;
if (size < 0) {
size = 0;
}
return makeMeasureSpec(size, mode);
}
}
复制代码
接着看 getRootMeasureSpec() 方法,传入的第一个参数是整个屏幕的宽或者高,第二个参数是 Window 的 LayoutParams:
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec;
switch (rootDimension) {
case ViewGroup.LayoutParams.MATCH_PARENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
break;
case ViewGroup.LayoutParams.WRAP_CONTENT:
measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
break;
default:
measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
break;
}
return measureSpec;
}
复制代码
从代码逻辑来看,DecorView 的 MeasureSpec 的产生遵循以下规律:
可是对于普通的 View 来讲,View 的 measure() 方法是由父容器 ViewGroup 调用的,看一下 ViewGroup 的 measureChildWithMargins() 方法:
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
// 获取子元素的布局参数
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
// 产生子元素的 MeasureSpec
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
复制代码
能够看出,在调用子元素 的 measure() 方法以前,先要调用 getChildMeasureSpec() 方法产生子元素的 MeasureSpec,子元素的产生除了跟父容器的 MeasureSpec 和子元素自己的 LayoutParams 有关以外,还与子元素的 Margin 和父容器的 Padding 值以及父容器当前占用空间有关,具体的过程能够看 getChildMeasureSpec() 方法:
public static int getChildMeasureSpec(int spec, int padding, int childDimesion) {
int specMode = MeasureSpec.getMode(spec);
int specSize = MeasureSpec.getSize(spec);
// 子元素最大可用空间为父容器的尺寸减去父容器中已被占用的空间的大小
int size = Math.max(0, specSize - padding);
int resultSize = 0;
int resultMode = 0;
switch (sepcMode) {
// Parent has imposed an exact size on us
case MeasureSpec.EXACTLY:
if (childDimension >= 0) {
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size. So be it.
resultSize = size;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimesion == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent has imposed a maximum size on us
case MeasureSpec.AT_MOST:
if (childDimension >= 0) {
// Child wants a specific size... so be it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size, but our size is not fixed.
// Constrain child to not be bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size. It can't be
// bigger than us.
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
break;
// Parent asked to see how big we want to be
case MeasureSpec.UNSPECIFIED:
if (childDimension >= 0) {
// Child wants a specific size... let him have it
resultSize = childDimension;
resultMode = MeasureSpec.EXACTLY;
} else if (childDimension == LayoutParams.MATCH_PARENT) {
// Child wants to be our size... find out how big it should be
resultSize = 0;
resultMode = MeasureSpec.UNSPECIFIED;
} else if (childDimension == LayoutParams.WRAP_CONTENT) {
// Child wants to determine its own size....
// find out how big it should be
resultSize = 0;
resultMode == MeasureSpec.UNSPECIFIED;
}
break;
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
复制代码
整个过程稍微有点复杂,能够参考如下表格:
回到 performTraversals() 方法中,获取到 DecorView 的 MeasureSpec 后接着会调用 performMeasure() 方法:
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
...
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
...
}
复制代码
mView 就是以前经过 setView() 方法传递过来的 DecorView 实例,它继承自 FrameLayout,而 FrameLayout 又是一个 ViewGroup 同时继承自 View。View 的 measure() 方法是 final 类型的,不容许子类去重写,所以这里调用的其实是 View 的 measure 方法:
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...
onMeasure(widthMeasureSpec, heightMeasureSpec);
...
}
复制代码
View 的 onMeasure() 实现:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
复制代码
能够看到,View 默认的 onMeasure() 方法首先会调用 getDefaultSize() 获取宽和高的默认值,而后再调用 setMeasuredDimension() 将获取的值进行设置,查看 getDefaultSize() 的代码:
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
复制代码
不管是 AT_MOST 仍是 EXACTLY,最终返回的都是 measureSpec 中的 specSize,这个 specSize 就是测量后的最终结果。至于 UNSPECIFIED 的状况,则会返回一个建议的最小值,这个值和子元素设置的最小值它的背景大小有关。
从 onMeasure() 的默认实现能够看出,若是咱们自定义一个直接继承自 View 的控件若是不重写 onMeasure() 方法,在使用这个控件并把 layout_width 或 layout_height 设置成 wrap_content 的时候,效果将会和 match_parent 同样!由于布局中使用 wrap_content 的时候,根据上面 getChildMeasureSpec() 方法总结出来的表格能够知道,此时的 specMode 是 AT_MOST,specSize 是 parentSize,而 parentSize 是父容器当前剩余的空间大小,此时 getDefaultSize() 就会返回 specSize,所以子元素的宽或高就被设置成了等于当前父容器剩余空间的大小了,这显然不符合咱们的预期,如何解决这个问题呢?一个通用的方案就是像以下方式重写 onMeasure() 方法:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
// 默认的宽/高
int mWidth = default_width;
int mHeight = default_height;
// 当布局参数设置为 wrap_content 时,使用默认值
if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT && getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(mWidth, mHeight);
// 宽 / 高任意一个布局参数为 wrap_content 时,都使用默认值
} else if (getLayoutParams().width == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(mWidth, heightSize);
} else if (getLayoutParams().height == ViewGroup.LayoutParams.WRAP_CONTENT) {
setMeasuredDimension(widthSize, mHeight);
}
}
复制代码
由于 DecorView 继承自 FrameLayout,它是一个 ViewGroup,ViewGroup 是一个抽象类,它并无定义一个具体的测量过程,默认使用 View 的 onMeasure() 进行测量。它的测量过程须要各个子类经过重写 onMeasure() 方法去实现,由于不一样的子类具备不一样的布局特性,所以须要不同的测量逻辑,DecorView 也天然重写了 onMeasure() 方法来实现本身的测量逻辑,简略后方法内容以下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
...
final int widthMode = getMode(widthMeasureSpec);
final int heightMode = getMode(heightMeasureSpec);
...
if (widthMode == AT_MOST) {
...
}
...
if (heightMode == AT_MOST) {
...
}
...
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
...
}
复制代码
最终会调用父类 FrameLayout 的 onMeasure() 方法,简略后方法内容以下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
...
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
...
}
}
...
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
...
}
复制代码
能够看到,这里会遍历它的每个子元素,并调用 measureChildWithMargins() 方法,这个方法其实前面已经出现过,它的做用是计算出子元素的 MeasureSpec 后调用子元素自己的 measure() 方法:
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
// 获取子元素的布局参数
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
// 产生子元素的 MeasureSpec
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+ widthUsed, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+ heightUsed, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
复制代码
此时实际上调用的也是 View 的 measure() 方法,从上面的内容能够知道,子元素的 onMeasure() 方法又会被调用,这样便实现了层层递归地调用到了每一个子元素的 onMeasure() 方法进行测量。
再次回到 performTraversals() 方法,执行完 performMeasure() 遍历测量全部子元素以后,接着会调用 performLayout() 方法:
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth, int desiredWindowHeight) {
...
// 调用 DecorView 的 layout() 方法
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
...
}
复制代码
这里的 getMeasuredWidth() 和 getMeasuredHeight() 就是 DecorView 在前面 Measure 流程中计算获得的测量值,它们都被做为参数传入 layout() 方法中,这里调用的是 View 的 layout() 方法:
public void layout(int l, int t, int r, int b) {
...
// View 状态是否发生了变化
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
// 若是 View 状态有变化,则从新布局
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
...
}
...
}
复制代码
setOpticalFrame() 内部也直接调用了 setFrame() 方法,查看 setFrame() 方法的实现:
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
...
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged);
// 变量初始化
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
// 更新用于渲染的显示列表
mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
...
if (sizeChanged) {
// 若是 View 大小发生变化,则会在里面回调 onSizeChanged 方法
sizeChange(newWidth, newHeight, oldWidth, oldHeight);
}
...
}
// 返回是否发生变化
return changed;
}
复制代码
setFrame() 方法的主要做用有如下几点:
回到 layout() 方法,根据 setFrame() 方法返回的状态判断是否须要调用 onLayout() 进行从新布局,查看 onLayout() 方法:
/** * Assign a size and position to a view and all of its * descendants * * <p>This is the second phase of the layout mechanism. * (The first is measuring). In this phase, each parent calls * layout on all of its children to position them. * This is typically done using the child measurements * that were stored in the measure pass().</p> * * <p>Derived classes should not override this method. * Derived classes with children should override * onLayout. In that method, they should * call layout on each of their children.</p> */
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
复制代码
onLayout() 方法是一个空方法,从注释最后一段内容能够了解到,单一的 View 并不须要重写这个方法,当 View 的子类具备子元素(即 ViewGroup)的时候,应该重写这个方法并调用每一个子元素的 layout() 方法,所以做为一个 ViewGroup,咱们查看 DecorView 的 onLayout() 方法:
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
...
}
复制代码
这里的主要逻辑是调用父类的 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) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
// 调用每一个子元素的 layout 方法
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
复制代码
在 layoutChildren() 方法中,根据本身的布局逻辑,计算出每一个子元素的 left、top、right 和 bottom 值,并调用它们的 layout() 方法。
Layout 流程的做用是 ViewGroup 用来肯定它的子元素的位置, 当 ViewGroup 的位置被肯定后,在它的 onLayout() 方法中就会遍历调用全部子元素的 layout() 方法,子元素的 layout() 方法被调用的时候它的 onLayout() 方法又会被调用,这样就实现了层层递归。
最后,又一次回到主线中的 performTraversals() 方法,此时,通过 Measure 流程肯定了每一个 View 的大小而且通过 Layout 流程肯定了每一个 View 的摆放位置,下面将进入下一个流程肯定每一个 View 的具体绘制细节。查看 performDraw() 方法内容:
private void performDraw() {
...
final boolean fullRedrawNeeded = mFullRedrawNeeded;
mFullRedrawNeeded = false;
mIsDrawing = true;
try {
draw(fullRedrawNeeded);
} finally {
mIsDrawing = false;
}
...
}
复制代码
跟踪 draw() 方法:
private void draw(boolean fullRedrawNeeded) {
...
// “脏”区域,即须要重绘的区域
final Rect dirty = mDirty;
...
if (fullRedrawNeeded) {
mAttachInfo.mIgnoreDirtyState = true;
dirty.set(0, 0, (int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
}
...
if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {
if (mAttachInfo.mHardwareRenderer != null && mAttachInfo.mHardwareRenderer.isEnabled()) {
...
// 调用 drawSoftware 方法
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {
return;
}
}
}
...
}
复制代码
查看 drawSoftware() 方法实现:
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff, boolean scalingRequired, Rect dirty) {
// Draw with software renderer.
final Canvas canvas;
try {
final int left = dirty.left;
final int top = dirty.top;
final int right = dirty.right;
final int bottom = dirty.bottom;
// 锁定canvas区域,由 dirty 区域决定
canvas = mSurface.lockCanvas(dirty);
...
canvas.setDensity(mDensity);
} catch (Surface.OutOfResourcesException e) {
handleOutOfResourcesException(e);
return false;
} catch (IllegalArgumentException e) {
mLayoutRequested = true; // ask wm for a new surface next time.
return false;
}
try {
...
try {
...
// 调用 DecorView 的 draw 方法
mView.draw(canvas);
...
} finally {
...
}
} finally {
mLayoutRequested = true; // ask wm for a new surface next time.
//noinspection ReturnInsideFinallyBlock
return false;
}
return true;
}
复制代码
继续查看 DecorView 的 draw() 方法:
@Override
public void draw(Canvas canvas) {
super.draw(canvas);
if (mMenuBackground != null) {
mMenuBackground.draw(canvas);
}
}
复制代码
主要是调用了父类的 draw 方法,FrameLayout 和 ViewGroup 都没有重写 draw() 方法,因此直接看 View 的 draw() 方法:
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);
// 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);
// 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);
// 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);
}
复制代码
从代码注释中能够看到,draw() 过程分为六个步骤,分别是:
其中第二步和第五步通常状况下不会用到,咱们继续看第三步,看看它是如何分配子元素的绘制的:
protected void dispatchDraw(Canvas canvas) {
}
复制代码
显然,单一的 View 并无子元素,所以,看看 ViewGroup 是怎么实现这个过程的:
@Override
protected void dispatchDraw(Canvas canvas) {
...
for (int i = 0; i < childrenCount; i++) {
while (transientIndex >= 0 && mTransientIndices.get(transientIndex) == i) {
final View transientChild = mTransientViews.get(transientIndex);
if ((transientChild.mViewFlags & VISIBILITY_MASK) == VISIBLE ||
transientChild.getAnimation() != null) {
more |= drawChild(canvas, transientChild, drawingTime);
}
...
}
...
}
...
}
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
复制代码
在 ViewGroup 的 dispatchDraw() 中,遍历每一个子元素并调用了它们的 draw() 方法,由此实现了层层递归调用,最终完成绘制。
纵观整个 Measure、Layout 和 Draw 过程,使用流程图表示以下:
不知道你还记不记得,上一步骤中执行的 View 的 Measure、Layout 和 Draw 流程都是前面 handleResumeActivity() 中的 wm.addView() 方法为源头的,回顾 handleResumeActivity() 方法:
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
ActivityClientRecord r = mActivities.get(token);
...
// 最终会调用 onStart() 和 onResume() 等方法
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
...
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
// 获取 DecorView
View decor = r.window.getDecorView();
// 将 DecorView 设置成不可见
decor.setVisibility(View.INVISIBLE);
// 获取 ViewManager,这里是 WindowManagerImpl 实例
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
...
if (a.mVisibleFromClient && !a.mWindowAdded) {
// 标记设置为 true
a.mWindowAdded = true;
// 调用 WindowManagerImpl 的 addView 方法
wm.addView(decor, l);
}
} else if (!willBeVisible) {
...
}
...
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
...
if (r.activity.mVisibleFromClient) {
// 调用 makeVisible 方法将 DecorView 设置为可见
r.activity.makeVisible();
}
}
...
} else {
try {
// 在此过程出现异常,则直接杀死 Activity
ActivityManagerNative.getDefault()
.finishActivity(token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
复制代码
能够看到在调用 wm.addView() 方法以前,DecorView 是处于不可见的状态的,所以,即便通过了 Measure、Layout 和 Draw 流程,咱们的 View 仍然没有显示在屏幕上,看看 Activity 的 makeVisible() 方法:
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
复制代码
执行 DecorView 的 setVisibility() 以后,咱们的 View 才正式出如今屏幕上!
系列文章
按下电源键后居然发生了这一幕 —— Android 系统启动流程分析
App 居然是这样跑起来的 —— Android App/Activity 启动流程分析
屏幕上内容到底是怎样画出来的 —— Android View 工做原理详解(本文)
参考文章
(3)自定义View Layout过程 - 最易懂的自定义View原理系列
若是你对文章内容有疑问或者有不一样的意见,欢迎留言,咱们一同探讨。