环境: android gradle 2.3

transform api

一 概况

transform 开始于1.5.0-beta1 可以用于在android 打包,class转换成dex 过程中,加入开发者自定义的处理逻辑. 他也可以处理native.只是native 这里处理的是so 文件的,再加工难度比较大.

二 定义

ContentType:
CLASSES,
RESOURCES,
DEX,
NATIVE_LIBS,
CLASSES_ENHANCED,
JACK
ContentType表示文件的类型. CLASSES 这个是javac 编译成class文件
RESOURCES: 这里的resources 单指Java 的资源.
DEX 这个是class 文件dx 编译成的dex 文件.
比较可惜的是自定义的 transform 无法处理这些文件. 具体原因看后面.
Scope:
PROJECT(0x01),
PROJECT_LOCAL_DEPS(0x02),
SUB_PROJECTS(0x04),
SUB_PROJECTS_LOCAL_DEPS(0x08),
EXTERNAL_LIBRARIES(0x10),
TESTED_CODE(0x20),
PROVIDED_ONLY(0x40)

通过 Scope 和ContentType可以组成一个资源流.即PROJECT 和CLASSES ,表示了主项目中java 编译成的class 组成的一个资源流,SUB_PROJECTS 和 CLASSES ,表示的是本地子项目中的java 编译的class 资源流.

三 transform 的作用

transform 是来处理和转换这些流的.
transform 中存在两种资源流,一种是会被消费掉.一种只是参与了转换过程.并不会被消费掉.

资源流存储在一个资源池. transform 从这个资源池收集这两种流.然后经过一定的规则转换生成新的资源流放到这个池子里. 同时将未消耗的资源流也放回这个池子里去,下一个transform 重复之前的流程.

api

Set<ContentType> getInputTypes():定义了你要处理的类型;
Set<Scope> getScopes():你要消耗资源流的范围;
Set<Scope> getReferencedScopes():转换过程中需要资源流的范围,在转换过程中不会被消耗,转换结束后, 会将资源流放回资源池去.
Set<ContentType> getOutputTypes() 转换输出类型,默认是getInputTypes()

四 transform工作原理

对外注册api:

1
2
android.registerTransform(new XTransform());
android.registerTransform(new XTransform(), dependencies)

内部注册api

1
TransformManager.addTransform();

gradle 会收集一些原始的资源流, 同时这些流可能还会有依赖task的,类似前置任务,比如说PROJECT 的CLASSES 需要依赖avac 的task 的任务.NATIVE_LIBS 需要依赖于ndk 的task ,这个也是可以理解的, 因为project 的class 产生是需要通过javac 的任务生成的. 同时如果你的transform 需要处理或者依赖这些资源流, 会被自动的被依赖上这些task.当gradle 收集完原始的资源流以后, gradle 开始注册transform,因为注销的transform是有顺序的,所以先注册的先处理资源流, 如果先注册的transform 消耗掉的资源.后续的transform 就无法处理了, 但是他可以处理前面transform 生成的资源流.(比方说有一个transformA 消耗了PROJECT 的CLASSES.同时经过转换生成了PROJECT 的CLASSES, 那下一个transformB如果要消耗和处理PROJECT 的CLASSES,那么他处理的是就是transformA转换的资源流而不是gradle收集资源流.)

自定义的transform之所以不能处理Dex 文件,是因为Dex 是由DexTransform|MultiDexTransform 由CLASSES 转成dex, 而自定义的transform 的注册在DexTransform|MultiDexTransform 之前,意思是自定义transform 注册的时候资源池里面还没dex的资源流.所以它无法处理Dex;

五 再讲资源流

说是流,其实是一个个文件的集合.原始的资源流是在Configuration 阶段中,收集成一个个文件的聚合. 而transform 生成的资源流是怎样的是,它其实是一个根目录(build/transforms/xxx)为基准,根据规则生成的一个个目录. gradle 通过TransformOutputProvider这个类帮我们简化这个步骤.

六 尾巴

  1. 虽然从getReferencedScopes() 可以获取到资源流,但是你不应该对这个资源流做任何的改动.因为这个可能作为下一个transform的输入. 同时这个资源流里面的文件.可能是一个全局的文件. 你的更改,将不仅仅影响到这个工程.甚至其他工程.
  • 颗粒太大:处理是的流,无法处理流里面的单个文件. 就是说如果你只是想单单处理某个文件. 你将不得不处理整个流. 造成多余的copy,以及磁盘的占用.
  • 自定义的transform 无法处理Dex
  • 自定义的transform 无法使用自定义ContentType