LayoutInflater源码解析

作者: 慕涵盛华 | 来源:发表于2017-11-11 12:56 被阅读69次

    最新在阅读《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小先生

    相关文章

      网友评论

      本文标题:LayoutInflater源码解析

      本文链接:https://www.haomeiwen.com/subject/ddkmmxtx.html