本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
参考码源:Android Oreo 8.0.0
工具:Layout Inspector
我们在Activity的onCreate()方法中设置setContentView(),但是一直不明白其中的原理,正好公司在开展技术交流活动,分到的课题是View、Activity、Window的关系,借这个机会梳理一番。Activity生命周期的调用时通过ActivityThread管控的,我们在设置应用页面时,都是在onCreate()中调用setContentView()加载布局,这样就产生了两个疑惑:
1、为什么要在onCreate()中设置。
2、setContentView是如何起作用的。
3、DecorView和PhoneWindow如何结合。
下面将通过源码的解读来分析这两个问题。
首先介绍一下如何在AndroidStudio中查看布局结构树,在Eclipse中,提供了工具hierarchyviewer.bat(在tools文件下),AndroidStudio将这个工具合并到自身中,并命名为Layout Inspector
使用步骤如下:
1、连接手机,并确保可以adb shell。
2、开启AndroidStudio,运行一个本地应用。
3、在菜单栏选择Tools->Android->Layout Inspector,弹出检索框,点击确定。
便会分析当前手机页面的布局结构。效果如图所示:
整个分析页面分为左中右三部分,左边是视图结构树,中间是页面展示,右边是选中视图的信息展示,我们需要关注的是视图结构树,对于视图信息我们关注红圈标记的mID就行了。
1、为什么要在onCreate()中设置。
借助LayoutInspector的分析,会发现应用布局的外层有一个根视图DecorView,
DecorView本质是一个系统自定义的FrameLayout。这个DecorView是如何出现在我们的布局中的呢?先从setContentView开始溯源找起。通过溯源会发现,在Activity中调用setContentView(int layoutID)实际上执行的是
Window.setContentView(int layoutID)。代码如下:
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
Window是一个抽象类,注解中声明Window是一个管理窗口外观和属性策略的抽象类,它的实现类将会以顶层视图的形式添加到窗口管理器中。它提供了标准的UI策略。且有一个唯一的实现类:PhoneWindow。重新回到Activity源码中搜索PhoneWindow,确实找到了这个类,同时也是getWindow()的返回值类型。注解中声明PhoneView所在包为android.view,但实际上通过检索PhoneView已经被移到了android.internal.policy下。
那么PhoneWindow对象是在哪里初始化的呢,在attach()中完成了实例化操作,
在ActivityThread的掌控中,初始阶段的调用顺序如下:
在一个Activity对象被创建的初期,会首先依靠WindowManagerGlobal和WWM建立通信关系,WindowManagerGlobal用来向WindowManagerService注册,主要是获取到 WindowManagerService 代理对象。对外提供与WindowManagerService(WWM)的底层通信。随后ActivityThread通过performLaunchActivity调用Activity生命周期,调用顺序如下:
Activity.attach()是Activity实例化后最先被调用的,这就保证了Window实例化对象的可用性。而onCreate()和onStart()是初始阶段唯一可以重写的方法,其他的都是final类型,鉴于Activity本质是管理页面交互,布局加载时机越早越有益于页面的展示。所以此时不设,更待何时呢。setConteneView(int layoutID)就在onCreate()中调用了。这样第一个问题就回答完了。
2、setContentView是如何起作用的