- 【Android】Window/DecorView/ViewRo
- Activity,window,DecorView,viewRo
- Activity,Window,DecorView,ViewRo
- window、Activity、DecorView、ViewRo
- Activity Window DecorView ViewRo
- Android Framework系列5-2 Activity显
- 番外 - Activity, DecorView, Window
- 日更挑战-Android Window、Activity、Dec
- 深入理解MeasureSpec
- Activity 与 Window、PhoneWindow、De
一、基本信息
Window
- Window是一个抽象类,具体实现只有一个
PhoneWindow
类。 - Window是一个抽象的概念,表示了一个窗口,是View Hierarchy(视图树)的容器;
- 对Window的操作只能通过WindowManager,最终经IPC由WindowManagerService最终实现;
- Window不直接管理View Hierarchy,而是通过ViewRootImpl;
- 事件分发由Window直接下发给DecorView,而不通过ViewRootImpl;
ViewRootImpl
- ViewRootImpl是View Hierarchy的根节点,是DecorView的parent,也是View Hierarchy的实际管理者;
- ViewRootImpl管理整棵View树的相关事件,包括但不仅限于View的测量、布局、绘制等;
- ViewRootImpl实现了View和WindowManager之间所需要的协议;
- 实际上,View树的
DecorView
- DecorView是最顶层的View,是所有View的祖先节点;
- DecorView是一个FrameLayout;
二、创建时机
Window
根据Activity启动流程,当流程进行到ActivityThread.performLaunchActivity
的时候,会创建Activity实例,并调用其attach
方法。在attach
方法中,会创建Window的实例。
final void performLaunchActivity(...args) {
activity = mInstrumentation.newActivity(...);
activity.attach(...);
...
}
final void attach(...args) {
mWindow = new PhoneWindow(this, window, activityConfigCallback);
...
}
也就是说,在Activity实例创建之初,Window就已经创建好了。
DecorView
DecorView在第一次调用Window.getDecorView
的时候被创建。PhoneWindow类的getDecorView
方法实现如下:
public final @NonNull View getDecorView() {
if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
}
一般情况下,在onCreate
回调中调用了setContentView
方法,DecorView就被初始化了。
调用链为:
AppcompatActivity.setContentView ->
AppCompatDelegateImpl.setContentView ->
AppCompatDelegateImpl.ensureSubDecor ->
AppCompatDelegateImpl.createSubDecor ->
PhoneWindow.getDecorView ->
PhoneWindow.installDecor
最终,在PhoneWindow.installDecor
方法中,DecorView被初始化:
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(-1);
...
}
protected DecorView generateDecor(int featureId) {
...
return new DecorView(context, featureId, this, getAttributes());
}
另外,如果不调用setContentView
,DecorView同样也会在Activity.performCreate
方法中较后的地方创建。
ViewRootImpl
ViewRootImpl是在WindowManagerGlobal的addView
方法中被初始化的,并且也是在这里与DecorView进行绑定,成为DecorView的parent。调用链可以追溯到ActivityThread.handleResumeActivity
中,在performResumeActivity
调用之后,ViewRootImpl被创建。
public void handleResumeActivity(...args) {
...
final ActivityClientRecord r = performResumeActivity(...); // onResume
ViewManager wm = a.getWindowManager();
wm.addView(decor, l); // ViewRootImpl实例创建
}
也就是说,ViewRootImpl是在onResume
回调之后才进行初始化的。这可以在onResume
中打印getWindow().getDecorView().getParent()
证实。
小结
- Window创建于
ActivityThread.performLaunchActivity
->Activity.attach
,在onCreate
之前; - DecorView一般创建于调用
setContentView
之后;如果不在onCreate
中调用setContentView
,DecorView会在onCreate之后(performCreate方法中)创建; - ViewRootImpl创建于
ActivityThread.handleResumeActivity
,在onResume
之后;
三、其他
1. 第一次测绘流程何时执行?
在二
中知道了,handleResumeActivity
方法最终会调用WindowManagerGlobal.addView
方法,在这里ViewRootImpl
被创建,并且通过setView
方法绑定DecorView,这些都发生在onResume之后。
在ViewRootImpl的setView
方法中,又会调用requestLayout
,在这里就会进行这个View树的第一次测绘。具体的方式是通过scheduleTraversals
方法向Choreographer
发送一个预定的消息,并在下一次屏幕刷新的时候调用doTraversal → performTraversals
方法进行ViewTree的测量、布局和绘制。
从这一点我们可以知道,在Activity的onResume
或之前的生命流程中调用View的getMesuredWidth
或者getWidth
都会返回0,因为这个时候还没有开始测量。
2. 为什么在onCreate中通过View.post
可以获取View的宽高?View.post
的流程是什么?
当View树还没有被测绘的时候,View.post会将这个Runnable发送给内部的一个消息队列(不是系统的消息队列)。这个消息队列中保存的Runnable会在下一次performTraversals
的时候被执行;调用链为:
ViewRootImpl.performTraversals
→
DecorView.dispatchAttachedToWindow
→
... →
View.dispatchAttachedToWindow
→
executeActions
最终在executeActions
方法中,Runnable对象被发送给ViewRootImpl的内部Handler执行。也就是说,当这个Runnable被执行的时候,已经至少经过一次测绘了,所以可以正确的获取到View的宽高;也说明了为什么通过View.post
发送的Runnable会在主线中执行。
当View树已经被测绘过了,View.post
就会直接通过内部的mHandler
发送消息,这个Handler是在attach的过程中跟着AttachInfo
一起传递给View的,实质上就是ViewRootImpl的内部Handler。
网友评论