環(huán)境: android gradle 2.3
一 概況
transform 開始于1.5.0-beta1 可以用于在android 打包,class轉(zhuǎn)換成dex 過程中,加入開發(fā)者自定義的處理邏輯. 他也可以處理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 是來處理和轉(zhuǎn)換這些流的.
transform 中存在兩種資源流,一種是會被消費掉.一種只是參與了轉(zhuǎn)換過程.并不會被消費掉.
資源流存儲在一個資源池. transform 從這個資源池收集這兩種流.然后經(jīng)過一定的規(guī)則轉(zhuǎn)換生成新的資源流放到這個池子里. 同時將未消耗的資源流也放回這個池子里去,下一個transform 重復(fù)之前的流程.
api
Set<ContentType> getInputTypes()
:定義了你要處理的類型;
Set<Scope> getScopes()
:你要消耗資源流的范圍;
Set<Scope> getReferencedScopes()
:轉(zhuǎn)換過程中需要資源流的范圍,在轉(zhuǎn)換過程中不會被消耗,轉(zhuǎn)換結(jié)束后, 會將資源流放回資源池去.
Set<ContentType> getOutputTypes()
轉(zhuǎn)換輸出類型,默認是getInputTypes()
四 transform工作原理
對外注冊api:
android.registerTransform(new XTransform());
android.registerTransform(new XTransform(), dependencies)
內(nèi)部注冊api
TransformManager.addTransform();
gradle 會收集一些原始的資源流, 同時這些流可能還會有依賴task的,類似前置任務(wù),比如說PROJECT 的CLASSES 需要依賴avac 的task 的任務(wù).NATIVE_LIBS 需要依賴于ndk 的task ,這個也是可以理解的, 因為project 的class 產(chǎn)生是需要通過javac 的任務(wù)生成的. 同時如果你的transform 需要處理或者依賴這些資源流, 會被自動的被依賴上這些task.當gradle 收集完原始的資源流以后, gradle 開始注冊transform,因為注冊的transform是有順序的,所以先注冊的先處理資源流, 如果先注冊的transform 消耗掉的資源.后續(xù)的transform 就無法處理了, 但是他可以處理前面transform 生成的資源流.(比方說有一個transformA 消耗了PROJECT 的CLASSES.同時經(jīng)過轉(zhuǎn)換生成了PROJECT 的CLASSES, 那下一個transformB如果要消耗和處理PROJECT 的CLASSES,那么他處理的是就是transformA轉(zhuǎn)換的資源流而不是gradle收集資源流.)
自定義的transform之所以不能處理Dex 文件,是因為Dex 是由DexTransform|MultiDexTransform 由CLASSES 轉(zhuǎn)成dex, 而自定義的transform 的注冊在DexTransform|MultiDexTransform 之前,意思是自定義transform 注冊的時候資源池里面還沒dex的資源流.所以它無法處理Dex;
五 再講資源流
說是流,其實是一個個文件的集合.原始的資源流是在Configuration 階段中,收集成一個個文件的聚合. 而transform 生成的資源流是怎樣的是,它其實是一個根目錄(build/transforms/xxx)為基準,根據(jù)規(guī)則生成的一個個目錄. gradle 通過TransformOutputProvider這個類幫我們簡化這個步驟.
六 尾巴
- 雖然從getReferencedScopes() 可以獲取到資源流,但是你不應(yīng)該對這個資源流做任何的改動.因為這個可能作為下一個transform的輸入. 同時這個資源流里面的文件.可能是一個全局的文件. 你的更改,將不僅僅影響到這個工程.甚至其他工程.
- 顆粒太大:處理是的流,無法處理流里面的單個文件. 就是說如果你只是想單單處理某個文件. 你將不得不處理整個流. 造成多余的copy,以及磁盤的占用.
- 自定義的transform 無法處理Dex
- 自定義的transform 無法使用自定義ContentType