【Android】Framework笔记——setContentView流程


setContentView

首先先来到了 Activity::setContentView,它其实调用了 getWindow().setContentView, Window 在我们这里的实现类是 PhoneWindow,所以最后来到了 PhoneWindow::setContentView

PhoneWindow::setContentView 中,若 DecorView 未进行初始化,首先会调用 installDecor 对 DecorView 进行初始化。

installDecor 首先会调用 generateDecor 初始化并创建 DecorView,之后调用 generateLayout 通过 findViewById(ID_ANDROID_CONTENT) 找到并返回了 mParentContent。之后就是一些如对 ActionBar 等的初始化。(ID_ANDROID_CONTENT 即为 android.id.content)

installDecor 完成后则会调用 mLayoutInflater.inflate(layoutResID, mContentParent) 这一方法对我们 setContentView 所传入的 layoutId 进行 inflate 操作。

inflate

在 inflate 中,它首先会构造出一个 Xml 解析器,之后便会开始对 xml 文件进行解析。它首先会寻找布局的 xml 的 START_TAG 从而找到整个布局的根节点,之后开始对 xml 中的元素进行处理。

首先它会判断根节点是否为 merge 节点,若是 merge 节点,则直接调用 rInflate 方法递归解析剩下的节点,若是普通布局则会调用 createViewFromTag 方法构建出一个 View,之后调用 rInflateChildren 对内层进行递归解析(其实内部就是调用了 rInflate 方法),最后将其添加到 parent 上

rInflate

其实 inflate 过程是一个递归的过程,其递归的具体实现就是通过 rInflate 方法,在 rInflate 中,它首先获取了当前这个标签的深度,之后不断向其内部走。

若发现了 name 为 requestFocus 的节点,则会调用 consumeChildElements方法

若发现了 name 为 tag 的节点,则会调用 parseViewTag 方法

若发现了 name 为 include 的节点,则会调用 parseInclude 方法

若发现了 name 为 merge 的节点,则会抛异常,因为 merge 只能作为根节点

普通节点则会调用 createViewFromTag 获取该节点对应 View,并对该节点调用 rInflateChildren 从而进行向内层的递归解析,之后将其添加到当前 View 上。

consumeChildElements

consumeChildElements 其实作用就是忽略这个节点剩下的内容,直到下一个新的 START_TAG,也就是不对其作处理。

parseViewTag

parseViewTag 方法主要的作用就是对其父 view 设置其属性中所指定的 key 及 value,之后调用 consumeChildElements 方法忽略剩下的内容

parseInclude

对于 include 节点,它会首先取出 layout 属性所对应的 layout 并为其创建一个 xml 解析器,之后对其进行解析。其解析过程与前面的 inflate 几乎一致: 对 merge 直接调用 rInflate 递归解析剩下的节点,对其他节点先调用 createViewFromTag 构建 View,之后调用 rInflate 递归解析剩下节点。

createViewFromTag

在 createViewFromTag 中首先会检查 mFactory2 是否为 null,不为 null 则直接通过 mFactory2::onCreateView 进行解析。

若为 null,则又会检查 mFactory 是否为 null,不为 null 则通过 mFactory::onCreateView 进行解析。

否则又会检查 mPrivateFactory 是否为 null,不为 null 则通过mPrivateFactory::onCreateView 进行解析。

若前面的条件都不满足则会进入其默认解析步骤,它会判断当前的 name 对应的是一个系统 View 还是一个自定义 View,判断的过程是通过判断 name 中是否含有 「.」,若含有「.」则说明它是一个自定义 View。

若为系统 View,会直接调用 LayoutInflater 的 onCreateView 方法进行 View 对象的创建。

若不为系统 View,则会调用 LayoutInflater 的 createView 方法进行 View 对象的创建

onCreateView

其实 onCreateView 的作用只是将系统 View 的 name 前补上 「android.view.」,之后又调用了 createView 进行 View 的创建

createView

createView 的过程主要是通过反射实现的,它先尝试从 sConstructorMap 中尝试拿出该 name 对应 View 的构造函数的缓存,若没有则会先通过 ClassLoader::loadClass 加载出对应 class 对象,之后通过 getConstructor 获取构造函数并放入缓存。

同时这里还会有一层对 Filter 的判断,若对应的 class 在 mFliter 中不允许被 inflate(onLoadClazz 返回 false),则会在此处抛出异常。

得到构造函数之后,便开始尝试通过构造函数来构建对应的 View 对象,将 context 及 attrs 两个参数填入后调用了 Constructor::newInstance() 方法从而创建了对应的 View。

此时有个对 ViewStub 的特殊处理,若该 View 为 ViewStub,则会调用其 setLayoutInflater 并拷贝一个对应 context 创建的 LayoutInflater 从而使其可以延迟加载。


Android Developer in GDUT