LayoutInflater源码解析

最新在阅读《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.java

PolicyManager中通过反射构建了Policy的实现类对象,这个类实现了IPolicy接口,通过这种方式将具体的Policy类对外隐藏实现。PolicyManager实际上是一个代理类,具体的实现是通过sPolicy对象实现的,也就是 “com.android.internal.policy.impl.Policy”。

Policy.java

通过上述代码我们看到LayoutInflater的具体实现类是PhoneLayoutInflater,我们继续查看PhoneLayoutInflater的源码:
PhoneLayoutInflater.java

代码不多,核心的代码就是重写了LayoutInflater的onCreateView方法,该方法就是在传进来的View前面加上前缀构成完整的路径。最后,根据完整的路径来构建对应的View。

具体是一个怎么样流程呢?以Activity的setContentView为例来说明:
Activity.java

Activity的setContentView实际上调用的是Window的setContentView,Window是一个抽象类,它的具体实现类是PhoneWindow,那我们继续看一下它的这个方法:

PhoneWindow.java

在分析之前我们先看一下Window的View层级图:

Window的View层级图

从上图我们看出DecorView会预先加载系统定义好的布局,这个布局又包裹了mContentParent,而这个mContentParent又包含我们自己定义的布局。在PhoneWindow的setContentView方法中也验证这一点,首先构建mContentParent对象,然后通过LayoutInflater的inflate方法将指定的布局添加到mContentParent中,那接下来我们就看看inflate方法:

LayoutInflater.java
上述的inflate方法主要有一下几步:
* 1.解析布局xml的根标签。
* 2.如果根标签是merge,那么就调用rInflate进行解析,rInflate会将merge下的所有子View直接添加到根标签中。
* 3.如果是根标签是普通标签,就调用createViewFromTag进行解析。
* 4.调用rInflate就系temp根元素下的所有子View,并将这些View添加到temp中。
* 5.返回解析到的根视图。

我们先看一下解析单个元素的方法createViewFromTag:

LayoutInflater.java

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,只写名字而不写完整的路径。那我们就看一下该方法的具体实现:

LayoutInflater.java

createVeiw的代码相对比较简单,如果有前缀就构造View的完整路径,并将该类加载到虚拟机中,然后获取该类的构造方法并加入缓存中,然后在通过构造方法创建该View的对象,最后将该View对象返回。这个是解析单个View,我们的窗口是一颗视图树,LayoutInflater需要解析这颗树,这个功能就交给了rInflate方法,具体的方法实现如下:
LayoutInflater.java

rInflate通过深度优先遍历来构造视图树,每解析到一个View元素就会递归调用rInflate,直到这条路径的最后一个元素,然后再回溯过来将每个View添加到它们的parent。通过rInflate解析之后,整颗视图树就构建完毕。当调用了Activity的onResume之后,我们通过setContentView设置的内容就会出现在我们的视野中。

关注微信公众号获取更多相关资源

Android小先生

猜你喜欢

转载自blog.csdn.net/guofudong2/article/details/81286997
今日推荐