最新在阅读《Android源码设计模式解析与实战》一书,我觉得写的很清晰,每一个知识点都有示例,通过示例更加容易理解。书中的知识点有些都接触过,有的没有接触过,总之,通过阅读这本书来梳理一下知识点,可能有些东西在项目中一直在使用,然并不能笼统,清理的说明理解它。本文主要是记录阅读这本书的知识点和自己的一些理解。一来整理知识点,二来方便以后查看,快速定位。
LayoutInflater在开发中扮演者非常重要的角色,会经常使用到,通过简单的API调用即可实现,使用非常的简单,那么它内部具体是怎么实现的呢?LayoutInflater本身是一个抽象类,我们需要把它“找出来”。
public abstract class LayoutInflater {
//代码省略
}
他是一个系统级别的服务,在加载ContextImpl时会通过如下的代码将LayoutInflater的ServiceFetcher注入到容器中:
registerService(LAYOUT_INFLATER_SERVICE, new ServiceFetcher() {
public Object createService(ContextImpl ctx) {
return PolicyManager.makeNewLayoutInflater(ctx.getOuterContext());
}});
这里调用了PolicyManager.makeNewLayoutInflater(ctx.getOuterContext()),继续往下看:
PolicyManager中通过反射构建了Policy的实现类对象,这个类实现了IPolicy接口,通过这种方式将具体的Policy类对外隐藏实现。PolicyManager实际上是一个代理类,具体的实现是通过sPolicy对象实现的,也就是 "com.android.internal.policy.impl.Policy"。
通过上述代码我们看到LayoutInflater的具体实现类是PhoneLayoutInflater,我们继续查看PhoneLayoutInflater的源码:
代码不多,核心的代码就是重写了LayoutInflater的onCreateView方法,该方法就是在传进来的View前面加上前缀构成完整的路径。最后,根据完整的路径来构建对应的View。
具体是一个怎么样流程呢?以Activity的setContentView为例来说明:
Activity的setContentView实际上调用的是Window的setContentView,Window是一个抽象类,它的具体实现类是PhoneWindow,那我们继续看一下它的这个方法:
在分析之前我们先看一下Window的View层级图:
从上图我们看出DecorView会预先加载系统定义好的布局,这个布局又包裹了mContentParent,而这个mContentParent又包含我们自己定义的布局。在PhoneWindow的setContentView方法中也验证这一点,首先构建mContentParent对象,然后通过LayoutInflater的inflate方法将指定的布局添加到mContentParent中,那接下来我们就看看inflate方法:
上述的inflate方法主要有一下几步:
- 1.解析布局xml的根标签。
- 2.如果根标签是merge,那么就调用rInflate进行解析,rInflate会将merge下的所有子View直接添加到根标签中。
- 3.如果是根标签是普通标签,就调用createViewFromTag进行解析。
- 4.调用rInflate就系temp根元素下的所有子View,并将这些View添加到temp中。
- 5.返回解析到的根视图。
我们先看一下解析单个元素的方法createViewFromTag:
createViewFromTag会将该元素的parent和名字传递过来,当这个tag的名字中没有包含“.”时,LayoutInflater会认为这是Android自带的View,例如我们在xml声明一个内置View大概是这样的:
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
这里的TextView就是xml元素的名字,因为就会调用onCreateView来解析这个TextView标签。当我们使用自定义View时,必须写完整的路径,如下所示:
<com.gfd.view.MyView
android:id="@+id/share_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
此时就会调用createView来解析,为什么要这样处理呢?它们又有什么不同呢?
在上面提到的PhoneLayoutInflater中,它重写了onCreateView方法,该方法就是在View的前面添加了“android.widget”,然后再传递给createView去解析。也就是说内置View和自定义的View最终都调用了createView就行解析,只是Google为了开发者在XML中更方便使用内置的View,只写名字而不写完整的路径。那我们就看一下该方法的具体实现:
createVeiw的代码相对比较简单,如果有前缀就构造View的完整路径,并将该类加载到虚拟机中,然后获取该类的构造方法并加入缓存中,然后在通过构造方法创建该View的对象,最后将该View对象返回。这个是解析单个View,我们的窗口是一颗视图树,LayoutInflater需要解析这颗树,这个功能就交给了rInflate方法,具体的方法实现如下:
rInflate通过深度优先遍历来构造视图树,每解析到一个View元素就会递归调用rInflate,直到这条路径的最后一个元素,然后再回溯过来将每个View添加到它们的parent。通过rInflate解析之后,整颗视图树就构建完毕。当调用了Activity的onResume之后,我们通过setContentView设置的内容就会出现在我们的视野中。