5月4日,据国外媒体报道,富士康集团计划大力发展半导体业务,最近其调整了公司架构,设立了一个“半导体子集团”,还准备进入半导体的制造环节,已经要求半导体业务集团展开有关建设两座12英寸芯片厂的可行性研究。
周一早上好!新的一周继续努力吧!
本篇来自 冷漠的学徒 的投稿,分享了从setContentView()角度分析View、Window、Activity三者关系,一起来看看!希望大家喜欢。
冷漠的学徒 的博客地址:
https://blog.csdn.net/lz8362
我们在Activity的onCreate()方法中设置setContentView(),但是一直不明白其中的原理,正好公司在开展技术交流活动,分到的课题是View、Activity、Window的关系,借这个机会梳理一番。Activity生命周期的调用时通过ActivityThread管控的,我们在设置应用页面时,都是在onCreate()中调用setContentView()加载布局,这样就产生了三个疑惑:
为什么要在onCreate()中设置。
setContentView是如何起作用的。
DecorView和PhoneWindow如何结合。
下面将通过源码的解读来分析这三个问题。
首先介绍一下如何在AndroidStudio中查看布局结构树,在Eclipse中,提供了工具hierarchyviewer.bat(在tools文件下),AndroidStudio将这个工具合并到自身中,并命名为Layout Inspector
使用步骤如下:
连接手机,并确保可以adb shell。
开启AndroidStudio,运行一个本地应用。
在菜单栏选择Tools->Android->Layout Inspector,弹出检索框,点击确定。
便会分析当前手机页面的布局结构。效果如图所示:
整个分析页面分为左中右三部分,左边是视图结构树,中间是页面展示,右边是选中视图的信息展示,我们需要关注的是视图结构树,对于视图信息我们关注红圈标记的mID就行了。
为什么要在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下。
在一个Activity对象被创建的初期,会首先依靠WindowManagerGlobal和WWM建立通信关系,WindowManagerGlobal用来向WindowManagerService注册,主要是获取到 WindowManagerService 代理对象。对外提供与WindowManagerService(WWM)的底层通信。随后ActivityThread通过performLaunchActivity调用Activity生命周期,调用顺序如下:
Activity.attach()是Activity实例化后最先被调用的,这就保证了Window实例化对象的可用性。而onCreate()和onStart()是初始阶段唯一可以重写的方法,其他的都是final类型,鉴于Activity本质是管理页面交互,布局加载时机越早越有益于页面的展示。所以此时不设,更待何时呢。setConteneView(int layoutID)就在onCreate()中调用了。这样第一个问题就回答完了。
setContentView是如何起作用的
Activity在attach()中实例化了PhoneWindow对象,并且进行了绑定操作,操作如下:
mWindow = new PhoneWindow(this, window);
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
setContentView(int layoutID)最终执行的是PhoneView.setContentView(int layoutID),源码如下:
@Override
public void setContentView(int layoutResID) {
// Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
// decor, when theme attributes and the like are crystalized. Do not check the feature
// before this happens.
if (mContentParent == null) {
<strong>installDecor();</strong>
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID