记录 WindowManager,Window,Activity,DecorView,ViewRootImpl
Window,Activity,View 的关系
- Activity 持有成员变量 PhoneWindow(继承自 Window)
- PhoneWindow 持有一个 DecorView(继承自 Framelayout)
- DecorView 内部持有两个 ViewGroup 成员变量 mContentRoot,mContentParent,mContentRoot 会根据 Widow Features 的属性选择合适的系统布局资源,inflate 之后添加到 DecorView 里面
- mContentRoot 的布局里面都会有一个 id 为 android.id.content 的 ViewGroup,这个会被 mContentParent 成员变量引用
- Activity#setContentView 实际上是把 View 加到了 mContentParent 里面
详细参考源码,或很久之前写的 Blog:Activity、Window、View的关系
ActivityThread 启动 Activity
ActivityThread#handleLaunchActivity {
final Activity a = performLaunchActivity(r, customIntent);
}
|
∨
ActivityThread#performLaunchActivity {
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor);
}
|
∨
Activity#attach {
mWindow = new PhoneWindow(this);
mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE), ...)
mWindowManager = mWindow.getWindowManager();
}
|
∨
Window#setWindowManager {
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
Window#getWindowManager {
return mWindowManager;
}
到此,Activity,Window,WindowManager 关系以及何时实例化都很清楚了。
- ActivityThread 在 performLaunchActivity() 方法里实例了将要启动的 Activity
- 在 Activity 实例的 attach 方法里面创建了 PhoneWindow 对象,赋值于成员变量 mWindow,
- 再通过系统服务创建 WindowManager 对象
- 最后通过 mWindow 的 setWindowManager() 和 getWindowManager(),使得 Window 和 Activity 都持有关联WindowManager 对象
- 当然,在 setWindowManager() 的时候,WindowManager 对象也持有了当前的 Window 对象(实际上是 WindowManagerGlobal 持有)
回到 ActivityThread#performLaunchActivity
在 Activity#attach
之后
mInstrumentation.callActivityOnCreate -> Activity#onCreate
DecorView 关联 WindowManager
在 ActivityThread#handleLaunchActivity
执行之后,接下去执行:
public void handleStartActivity(ActivityClientRecord r, PendingTransactionActions pendingActions) {
Activity#performStart -> mInstrumentation.callActivityOnStart(this) -> Activity#onStart
(可能执行) mInstrumentation.callActivityOnRestoreInstanceState -> Activity#onRestoreInstanceState
mInstrumentation.callActivityOnPostCreate -> Activity#onPostCreate
}
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {
ActivityThread#performResumeActivity -> Activity#onResume()
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
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) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
} else {
// The activity will get a callback for this {@link LayoutParams} change
// earlier. However, at that time the decor will not be set (this is set
// in this method), so no action will be taken. This call ensures the
// callback occurs with the decor set.
a.onWindowAttributesChanged(l);
}
}
......
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
到此,Window 里面的 DecorView 也出现了,并且添加到了 WindowManager 里面
一处小细节:DecorView 最开始是 INVISIBLE,添加到管理后才通过 activity.makeVisible()
变成了 VISIBLE
深入 WindowManager
WindowManager 的实现类 WindowManagerImpl#addView 实际上是把 View 交给了 WindowManagerGlobal
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
WindowManagerGlobal#addView
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);
}
WindowManagerGlobal 最后是把 DecorView 交给 ViewRootImpl
ViewRootImpl
The top of a view hierarchy, implementing the needed protocol between View and the WindowManager. This is for the most part an internal implementation detail of WindowManagerGlobal.
层次结构的顶部,实现 View 和 WindowManager 之间需要的协议。这是 WindowManagerGlobal 内部实现内容的最大部分
理解上述描述,先要理解 WindowManager 是什么
每个 WindowManager 实例会绑定一个特定的 Display,Display 可以理解为屏幕的逻辑类,或者抽象类。结合之前的分析,Activity 启动过程中新建了 Window,Activity 通过 setContentView() 方法使 Window 创建了 DecorView 和其子 ViewGroup,最后在 ActivityThread 执行 handleResumeActivity 过程中(Activity#onReume 之后),又把 DecorView 全部交给了 WindowManger,显然是需要 WindowManger 作为中间人把 View 显示到屏幕上
因此描述可以理解为,WindowManger 会调用 ViewRootImpl 来告诉 View 要测量,绘制,以及最后通知系统服务,安排展示
ViewRootImpl 源码小结
ViewRootImpl#setView
|
∨
ViewRootImpl#requestLayout
|
∨
ViewRootImpl#checkThread() 检查是否是当前线程
ViewRootImpl#scheduleTraversals
|
∨
mChoreographer.postCallback(mTraversalRunnable) 异步 Handler 请求
|
∨
mTraversalRunnable.run().doTraversal()
|
∨
ViewRootImpl#performTraversals 完成测量,布局,绘制。
三个重要的方法:
- performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
- performLayout(lp, desiredWindowWidth, desiredWindowHeight);
- performDraw();
之后 ViewRootImpl#setView 内部调用 mWindowSession#addToDisplay,进行一次 IPC,告诉 WindowMangerService 展示 Window
ViewRootImpl 解释了几个问题
- 为什么 Activity#onCreate 的时候,View#getMeasured* 为 0。因为测量布局绘制的过程是在 Activity#onResume 之后才提交的,且是异步的。
- 那 Activity#onResume 里面调用 View#getMeasured* 就不为 0 了吗,不一定,因为是异步的,这是不可靠的
-
那什么时候可以确定 View#getMeasured* 测量的完成。测量之后,绘画之前会执行:
mAttachInfo.mTreeObserver.dispatchOnGlobalLayout();
对应可用回调:view.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { } });
- Activity#onCreate() 里面其他线程更新 UI 不会抛出异常,但是 view 显示后,其他线程更新 UI 会抛出异常。原因是检查线程的判断在 ViewRootImpl 里,但是 ViewRootImpl 实例的创建却在 ActivityThread#handleResumeActivity
- View#getViewRootImpl 是否公用同一个对象。是的
public ViewRootImpl getViewRootImpl() {
if (mAttachInfo != null) {
return mAttachInfo.mViewRootImpl;
}
return null;
}
ViewRootImpl 对象保存在 mAttachInfo 里面,AttachInfo是保存 view 和 Window 之间信息的类,在 ViewRootImpl 构造器里面创建。通过 mView#dispatchAttachedToWindow(mAttachInfo, 0) 传给 DecorView 及其子 View
ViewRootImpl.setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
...
mView = view;
...
}
ViewRootImpl.performTraversals() {
...
View host = mView
host.dispatchAttachedToWindow(mAttachInfo, 0);
...
}