ASO114

基于Android6.0的Activity加载View源码分析

赵二狗 • 2月前发布

今日科技快讯

昨日,小米号称“新一代性能怪兽”的小米6正式亮相,中国首发骁龙835+标配6GB内存。目前只能买到白色、黑色与蓝色版本,炫酷的亮银探索版,由于只能在真空环境中制造,所以还无法量产。首批小米6将于4月28日10点首发,销售渠道包括小米商城、小米之家、天猫、京东、苏宁易购以及线下核心专区门店。

作者简介

本篇来自程序媛 fanfan_story 的投稿,透过Android6.0源码,分析了Activity加载View的过程,希望大家喜欢。

fanfan_story 的博客地址:

http://blog.csdn.net/zrf1335348191

认识Activity的布局

对于研究布局这种东西,必须要掌握一些视图工具,在这里推荐一个sdk查看视图的工具 sdk oolshierarchyviewer,随意找一个界面去查看 activity 的 view视图:

在这个 activity 界面中我把导航栏给隐藏了,所以不存在导航栏,根据这张图的话大致可以看到一个 activity 的布局,再结合对 setContentView 的研究,可以总结出 activity 的布局图如下:

从这张 activity 的布局图可以看到:一个 activity 对应一个应用窗口 mWindow,应用窗口 mWindow 包括 activity 的 顶级view是 mDecorView,mDecorView 包括 状态栏statusbar和 导航栏navigationbar 以及要加载 activity 布局的view一一mDecorContentParent,该 view 又包括一个 标题栏titlebar 和 activity的内容布局 contentparent。在 contentParent 中就是该 activity 的 view树。

1. mWindow:Window对象,Window是一个抽象类,是 activity 的顶层外观和行为的代理。会往 windowmanage 中添加该类的一个实例作为 顶层view。window 提供基本的UI代理,比如背景啊,标题区域啊,按键处理啊等等,Window 只有一个实现类 PhoneWindow,所以 mWindow对象 实际是 PhoneWindow对象。当启动一个activity的过程中会初始化一个属于 Phonewindow 的 window对象。Phonewindow对象 的创建在activity 的 attach方法 中。

2. mDecor:DecorView对象,继承自 framelayout,是 window窗口 的 顶级view,包含 window 的装饰。类的定义位于 PhoneWindow.java 中

3. mDecorContentParent:DecorContentParent对象,实现类是 ActionbarOverlayLayout,属于 activity 布局的 最外层view,包括标题栏和activity的内容布局

4. mContentParent:activity 的内容布局,继承自 ViewGroup,用来加载存放 activity 的 view树,如果没有标题栏,那么 mContentparent 的大小会和 mDecorContentParent 相等,以此类推

5. 导航栏:statusbar,对应的 id 为 statusBarBackground,在 PhoneWindow 中会加载,当 window属性 发生改变时会刷新导航栏。但不论是导航栏和状态栏,从这个id也可以看出,PhoneWindow 只是加载他们的 background,即相当于只加载一个view的占位,先告诉应用窗口,系统窗口要求将状态栏和导航栏布局在这里,你不要占用,但此时不会加载导航栏和状态栏的view,只是绘制背景而已。

6. 状态栏:navigationbar,对应的 id 为 navigationBarBackground,在 PhoneWindow 中会加载,当 window属性 发生改变时会刷新状态栏

7. 标题栏:titlebar,对于导航栏,状态栏和标题栏的存在与否,与 window 的属性特征有关,在加载 view 时所以会去判断 window 的属性特征,进而决定是否要加载这三者。

对 activity 的布局大致有个了解之后,就开始去分析 activity 启动后加载view的流程

Activity加载View布局

对于 activity 的布局的加载大致分为两部分,一部分是加载view,另一部分是将view绑定到应用窗口Window。其中这两个步骤中将 view 绑定到 window 是在 启动activity时 完成的操作,是将 mDecor 绑定到 window。然后再往 mDecor 中添加 各种view。对于 activity的启动过程 留待以后进行分析,现在分析加载view一一始于 Activity.Java 的 setContentView 方法,看一下加载view的流程。

可以看到代码流程很简单,从 Activity.java 的 setContentView方法 进入,到 PhonewWindow.java 的 setContentView方法 进行一系列处理,接下来进入代码进行分析

Activity.java 的 setContentView 方法

代码路径 Androidframeworksasecorejavaandroidapp

源码中对该方法的解释是,从一个 layout 文件中取出 view 设置成 activity 的 content,该资源文件会被填充,并遍历文件中的 所有view 添加到 activity。意思就是填充一个资源文件,加载view。做了两件事儿:

  • 一是 getWindow 获取到 Window对象,然后去调用 Window 的 setContentView方法。

  • 二是 initWindowDecorActionbar(),创建 actionbar对象,填充 mDecor 下的 actionbarView,并把 view 加载上去(博主猜测是在 Window 的 setContentView方法 中只是填充一个 actionbar 的占位,然后 initWindowDecorActionbar() 完成 view 的加载)

重点研究第一步:getWindow().setContentView方法。

首先一个问题,为什么我要说 getWindow.setContentView 调用的是 PhoneWindow 中的setContentView方法?

解疑:查看getWindow方法:

public Window getWindow() {    return mWindow;
}

返回的是 activity 的 mWindow对象,对于 mWindow对象 的创建也是在 Activity.java 中的 attach方法 中:

同时,进入到 Window.java 中也可以看到这一点:

源码中对于 Window类 的说明是:

Window 是一个抽象类,是最顶层的窗口的外观和行为的代理,window 的实例应该被作为最顶层的UI添加到 WindowManage 中。Window 提供了基本的ui,比如背景,标题区域,默认的按键处理过程等等。Window 只有一个唯一的实现类 PhoneWindow,当需要 Window对象 时需要去初始化 PhoneWindow。

至此,对于 Activity 中的 mWindow对象 大致有了一个清晰的认识了:他是个 PhoneWindow对象,Window.java 中方法的实现在 PhoneWindow.java 中

PhoneWindow.java中的setContentView方法

代码路径 androidframeworksasecorejavacomandroidinternalpolicy

先来总结一下代码的流:

在新启动一个 activity 时 mContentParent 还未绑定id,此时 mContentParent 为 null。从代码流程图中可以看出 setContentView 做了三件事:

  • installDecor 实例化 DecorView对象 和 mContentParent对象

  • 填充 layout 文件

  • 通知 activity 布局已经改变

为什么说是通知 activity 布局已经改变呢?这是因为在 Activity.java 的 attach方法 中 mWindow对象 设置了 callback 为 this,所以在 getCallback 时获取到的 cb 为当前与该 window 对应的 activity。

PhoneWindow.java中的installDecor方法分析

实例化 DecorView对象 和 mContentParent对象

在创建一个 activity 时 mDecor 和 mContentParent 均为 null

  • 借助 generateDecor 方法实例化 mDecor,即获取到 activity 的 最顶级的view

  • 借助 generateLayout 方法实例化 mContentParent对象,并且根据 window 的不同 Feature 来选择对应的布局文件(总之,generatelayout 其实就是根据当前的 window 的特征属性 feature 来加载内容布局,并获取到当前布局的 最外层view。也就是说

  • generatelayout 本质就是根据 activity 的 theme主题 来找到对应的xml布局并且获取到id为 content 的 ViewGroup赋给 mContentParent

  • 获取到 mDecorParent对象,并且根据 getLocalFeature 获取到的 Feature 来设置(这也就说明了在自定义Activity时为什么要将 getWindow.requestFeature方法 写在 setContentView方法 之前)

  • 对 title 进行隐藏或者是设置内容的操作

  • 如果需要切入切出动画,那么就获取到各种动画资源

接下来对 installDecor 中某些代码做一些分析:

mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);

用于对焦点的传递设置:只有当 子view 不想获取焦点时 mDecor 才会去获取焦点

mDecor.postOnAnimation(mInvalidatePanelMenuRunnable)

去开启初始化menu菜单的线程

在这里说明一句,为什么 requestFeature 要写在 setContentView 前面,这是因为在调用 setContentView 时会获取到 window 的各种 feature 进行一些判断设置。

PhoneWindow.java中的generateLayout方法研究

第一步:首先是获取到 window 的布局style

TypedArray a = getWindowStyle();

第二步:获取到各种属性并进行 requestFeature 的设置

第三步:通过获取到的 window 的布局去获取 window 的各种属性,并根据 window 的各种属性去选择不同的 layout 的文件,比如标题栏是否隐藏,window 是悬浮窗还是全屏等等问题。当然因为在3.0和4.0以及5.0对于menukey的支持不同,所以会有一个与版本相关的一个判断。至于这个版本之间有什么不同可以参考总结说明中列出来的文件。

其实 generatelayout 就做了一件事,那就是根据 window 的各种属性去获取不同的xml文件。

总结
  • setContentView 执行流程中主要涉及到3个类 PhoneWindow.java,Activity.java 和 Window.java

  • Window 和 windowmanager 中的 各种feature 和 flag的style 对应的各种含义以及动画 style在androidandroidframeworksasecore es esvaluesattrs.xml 文件中有注释说明

  • 在 menu键 的设置中涉及到了版本问题,包括 3.0,4.0 和 5.0 分别有对应的不同处理,参考 androidandroidframeworksasecorejavaandroidosBuild.java 可以看到注释有说明各版本有什么不同

  • 至于为什么说 mDecor 是 最外层view,是因为在 generateLayout 方法中 mDecor 将填充该xml文件的 view一一mContentRoot 添加了进来。

Activity在启动加载布局的操作

  • 创建 DecorView 的布局:setContentView 的流程基本是用来创建 DecorView 的布局

  • 将布局添加到 window 窗口:在 Activity 的启动过程中,会将应用窗口添加到 WindowManager 中进行统一管理,以及绑定 DecorView

  • 对于状态栏和导航栏,是在每次 window 属性发生变化时会去更新,但是只是设置了一个背景色,只是占位用,没有加载这些view

更多

每天学习累了,看些搞笑的段子放松一下吧。关注最具娱乐精神的公众号,每天都有好心情。

如果你有好的技术文章想和大家分享,欢迎向我的公众号投稿,投稿具体细节请在公众号主页点击“投稿”菜单查看。

欢迎长按下图 -> 识别图中二维码或者扫一扫关注我的公众号:

30
标签:
Android
创业者们,如果你或你的朋友想被优派网报道,请狠戳这里    寻求报道
赵二狗驻站作者
作者简介
0
文章总数
0万
总阅读量
ASO114市场数据分析平台
>