编译 XML 模板

一个业务组件对应着一份 XML 模板,一个模板单独编译成二进制数据,它会包含除内置字符串资源以外所有它依赖的所有字符串资源、表达式资源。

在编译的时候,所有模板里涉及到的资源包括颜色值、各种枚举、基础组件的类型等都会被序列化映射成整数,而不能序列化成整数的资源比如字符串,就给它分配一个索引 Id 指向它,并将它们单独存储到一块区域里。序列化的规则如下:

  • 颜色:转换成4字节整型颜色值,格式 AARRGGBB;
  • 枚举:按照预定义的整数转换,比如 gravity 的类型,orientation 的类型…
  • 基础组件的类型:按照预定义的整数转换,内置基础组件的类型从 1 开始分配,自定义组件建议从 1000 开始分配,后续可能语义化成字符串类型定义,对用户隐藏这些细节;
  • 字符串:以 hashCode 值作为它的序列化后整数,并在字符串资源区建立以 hashCode 为索引的列表,在解析的时候从中获取原始的字符串值;
  • 逻辑表达式:与字符串的处理类似;

这就是为什么后面开发自定义基础组件的时候,需要自己定义大量的整数常量来服务编译和解析流程;

一个模板文件的编译流程大致如下:

编译流程.jpg

  1. 创建一个文件对象,编译工具开始编译模板的时候,先在创建一个输出文件的对象,指向特定路径,后续编译过程中的数据都写到这个文件里。
  2. 写入ALIVV、版本号数据,按照文件格式,开头5字节固定未ALIVV,可先写入,紧接着6个字节是3位版本号,主版本号固定为1,次版本号固定未0,修订版本号每次编译的时候开发人员通过参数传入,从1开始。
  3. 写入各区域的占位空间,根据文件格式,接下来32个字节分别为组件区、字符串区、表达式区、数据区的起始位置值和长度,所以先占位,初始化为0。还有当前文件页面编码、以及它的依赖,这也是编译时用户传入,默认页面编码为1,如果没有依赖的页面,这一部分不占空间。
  4. 读取一个原始模板文件,一个业务组件对应着一个模板,先读取一个原始模板数据。
  5. 创建XML解析器,因为原始模板是XML格式,使用XML解析器来解析其中的内容,XML解析器会按照XML的格式获取到每个节点以及它的属性,所以接下来只要遍历这些节点和属性来序列化原始数据。
  6. 开始遍历,先获取一个节点名,先记录节点开始标记。
  7. 根据节点名字符串,先创建对应的基础组件编译器对象,在编译工具里,每一个基础组件都注册了对应的编译器类型。用户开发自定义基础组件,也要提供自定义编译器注册到编译工具里。基础组件和对应的编译器类通过组件类型关联起来。
  8. 获取该基础组件下所有属性,开始遍历属性并处理。
  9. 每获取到一个基础组件属性,就调用编译器处理属性,编译器知道每个属性应该如何处理,因为这是定义属性、开发编译器类的时候确定的,每一种属性都会被序列化成以下4种类型:int整型、float浮点型、string字符串型、表达式类型,前两者直接作为序列化后的值写到返回结果里,后两者先通过hashCode为一个4字节索引作为序列化后的值写到返回结果里,真实的内容存储到临时列表里,后面会存储到单独的资源区。
  10. 遍历完当前节点所有属性。
  11. 按照整型、浮点型、字符串、表达式四种类别归类属性,按照4字节key索引、4字节value索引存到内存里。
  12. 当前节点处理完毕,写入一节点结束标记。检查是否遍历晚所有节点,如果还有其他节点,回到第6步开始处理新的节点,如果没有,开始下一步准备写入文件
  13. 将第11步序列化后的组件数据写入到文件,将第9步里存储的字符串和表达式资源分别依次写入到文件。
  14. 这样组件区、字符串区、表达式区的起始位置都知道了,就可已更新第3步里预留的空白区域。
  15. 如果有扩展数据,可以在表达式区后面写入扩展数据,目前做保留。
  16. 全部写完之后所有数据输出到文件,文件后缀为 .out

最新的: