一文读懂 View & Window 机制(二)
六、DecorView
DecorView 是 FrameLayout 的子类,其 onResourcesLoaded
方法在拿到 PhoneWindow 传递过来的 layoutResource
后,就会生成对应的 View 并添加为自己的 childView,就像普通的 ViewGroup 通过 addView
方法来添加 childView 一样,该 childView 就对应 mContentRoot
,我们可以在 Activity 中通过(window.decorView as ViewGroup).getChildAt(0)
来获取到 mContentRoot
所以 DecorView 可以看做是 Activity 中整个视图树的根布局
public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
@UnsupportedAppUsage
private PhoneWindow mWindow;
ViewGroup mContentRoot;
DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
···
}
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
if (mBackdropFrameRenderer != null) {
loadBackgroundDrawablesIfNeeded();
mBackdropFrameRenderer.onResourcesLoaded(
this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
getCurrentColor(mNavigationColorViewState));
}
mDecorCaptionView = createDecorCaptionView(inflater);
final View root = inflater.inflate(layoutResource, null);
if (mDecorCaptionView != null) {
if (mDecorCaptionView.getParent() == null) {
addView(mDecorCaptionView,
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mDecorCaptionView.addView(root,
new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
} else {
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
mContentRoot = (ViewGroup) root;
initializeElevation();
}
}
复制代码
七、ActivityThread
完成以上步骤后,此时其实还只是完成了 Activity 整个视图树的加载工作,虽然 Activity 的 attach
方法已经创建了 Window 对象,但还需要将 DecorView 提交给 WindowManager 后才能正式将视图树展示到屏幕上
DecorView 具体的提交时机还需要看 ActivityThread 的 handleResumeActivity
方法,该方法用于回调 Activity 的 onResume
方法,里面就会回调到 Activity 的makeVisible
方法,从方法名就可以猜出来makeVisible
就用于令 Activity 变为可见状态
@Override
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {
···
r.activity.makeVisible();
···
}
复制代码
makeVisible
方法会判断当前 Activity 是否已经将 DecorView 提交给 WindowManager 了,如果还没的话就进行提交,最后将 DecorView 的可见状态设为 VISIBLE,至此才建立起 Activity 和 WindowManager 之间的关联关系,Activity 也才正式变为可见状态
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
复制代码
八、做下总结
对以上流程做下总结
- 每个 Activity 内部都包含一个 Window 对象,该对象的具体实现类是 PhoneWindow。Activity 的
setContentView
、findViewById
等操作都会交由 Window 来实现,Window 是 Activity 和整个 View 系统交互的入口 - PhoneWindow 根据 theme 和 features 得知 Activity 的基本视图属性,由此来选择合适的根布局文件
layoutResource
,每种layoutResource
虽然在布局结构上略有不同,但是均会包含一个 ID 名为content
的 FrameLayout,ContentParent
即该 FrameLayout。我们可以通过Window.ID_ANDROID_CONTENT
来拿到该 ID,也可以在 Activity 中通过findViewById<View>(Window.ID_ANDROID_CONTENT)
来获取到ContentParent
- PhoneWindow 并不直接管理视图树,而是交由 DecorView 去管理。DecorView 会根据
layoutResource
来生成对应的 rootView 并将开发者指定的 ContentView 添加为ContentParent
的 childView,所以可以将 DecorView 看做是视图树的根布局。正因为如此,Activity 的findViewById
操作实际上会先交由 Window,Window 再交由 DecorView 去完成,因为 DecorView 才是实际持有 ContentView 的容器类 - PhoneWindow 是 Window 这个抽象类的的唯一实现类,Activity 和 Dialog 内部其实都是使用 PhoneWindow 来加载视图树,因此 PhoneWindow 成为了上层类和视图树系统之间的交互入口,从而也将 Activity 和 Dialog 的共同视图逻辑给抽象出来了,减轻了上层类的负担,这也是 Window 机制存在的好处之一
- Activity 的视图树是在
makeVisible
方法里提交给 WindowManager 的,之后 WindowManagerImpl 会通过 ViewRootImpl 来完成整个视图树的绘制流程,至此 Activity 才对用户可见 - View 通过 Canvas 绘制自身,定义了具体的 UI 效果。View 和 ViewGroup 共同组成一个具体的视图树,视图树的根布局则是 DecorView,DecorView 的存在使得视图树有了一个统一的容器,有利于统一系统的主题样式并对所有 childView 进行统一管理。Activity 通过 Window 和视图树进行交互,将具体的视图树处理逻辑抽取给 PhoneWindow 实现,减轻了自身负担。PhoneWindow 拿到 DecorView 后,又通过 ViewRootImpl 来对 DecorView 进行管理,由其来完成整个视图树的 Measure、Layout、Draw 流程。当整个视图树绘制完成后,就将 DecorView 提交给 WindowManager,从而将 Activity 显示到屏幕上
九、一个 Demo
这里我也提供一个自定义 Window 的 Demo,实现了基本的拖拽移动和点击事件,代码点击这里:AndroidOpenSourceDemo
十、一文系列
最近比较倾向于只用一篇文章来写一个知识点,也懒得总是想文章标题,就一直沿用一开始用的一文读懂XXX,写着写着也攒了蛮多篇文章了,之前也已经写了几篇关于 View 系统的文章,希望对你有所帮助 😇😇
作者:业志陈
链接:https://juejin.cn/post/6942303848996274213
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。