Android 的布局加载原理
- 首页调用
Activity的setContentView方法,内部最后调用到了AppCompatDelegateImpl的setContentView方法。 - 上面有两种情况,一种是直接设置的 View,一种是设置 layout.id。但是无论是哪种情况,从 xml 转化到对应的 Java 文件,都需要走到
LayoutInflater的inflate方法。 - 在
inflate方法中先会创建XmlResourceParser对象,它的作用就是解析 xml 标签 - 然后判断开始标签是否为
merge,如果不是,则先使用createViewFromTag创建父标签对象;否则直接调用rInflate方法以循环遍历的方式来创建 View 对象。需要注意的是,rInflate方法内部,也是通过createViewFromTag方法来创建对应的 View 的 - 在
createViewFromTag方法中,我们首先会先判断Factory2是否存在,如果存在就调用它的createView方法;否则判断Factory是否存在,如果存在就调用它的createView方法。如果都不存在就会调用默认的Factory2的createView方法。在该方法中是通过反射来实现创建 view 对象的。 - 在
rInflate方法中,通过createViewFromTag方法创建完所有的 view 对象后,整个布局加载流程就结束了。
性能瓶颈
从上面的 Android 的布局加载原理中可以看出,布局加载流程主要有两个性能的瓶颈:
- 布局文件加载过程。布局加载过程是一个IO操作,如果 xml 布局太大,就会拖慢View的创建速度。
- 创建View对象过程。默认情况下,创建对象是通过反射实现,如果需要创建的 View 非常多,也会影响性能
优化
上面分析到,Android 的布局加载原理主要有两个。因此,我们就可以从这两方面来优化这个问题。
- 设置
Factory和Factory2,从上面的加载原理可以知道,Android 会先判断Factory和Factory2是否存在,如果存在就直接使用它的createView方法,这样就不会使用反射来调用了。 - 不使用 xml 来创建布局,而是通过 java 来直接创建,但是这种方式不便于开发,可维护性差。我们还可以通过
x2c框架来实现,它会编译区间编译成Java代码,但是它也有不足的地方,比如说部分属性Java不支持 - 由于布局加载是一个 IO 过程,我们可以让它在子线程去执行,不阻塞主线程。在 Android 中提供了
AsyncLayoutInflater来实现这个效果。但是它也有两个缺陷,一个是他最多可以处理 10 个加载任务,超过 10个的任务还是会在主线程执行;另一个是,它无法设置 Factory - 其他的优化手段,就只有从层级入手了。比如说,使用
merge标签,减少一层嵌套;使用ConstraintLayout替代RelativeLayout或者LinearLayout等等