Gradle Transform API 的基本使用

文章來(lái)源自作者的Android進(jìn)階計(jì)劃(https://github.com/SusionSuc/AdvancedAndroid)

在前面學(xué)習(xí)WMRouterARouter時(shí)都涉及到了Transform API。他們都利用Transform在編譯生成class文件之后和生成dex文件之前來(lái)做一些事情修档,本文就具體了解一下Gradle Transform API卧惜。

什么是Transform

官方API文檔: http://google.github.io/android-gradle-dsl/javadoc/2.1/com/android/build/api/transform/Transform.html

我們編譯Android項(xiàng)目時(shí)拥刻,如果我們想拿到編譯時(shí)產(chǎn)生的Class文件齿椅,并在生成Dex之前做一些處理灸蟆,我們可以通過(guò)編寫(xiě)一個(gè)Transform來(lái)接收這些輸入(編譯產(chǎn)生的Class文件),并向已經(jīng)產(chǎn)生的輸入中添加一些東西曼验。

我們可以通過(guò)Gradle插件來(lái)注冊(cè)我們編寫(xiě)的Transform卡骂。注冊(cè)后的Transform會(huì)被Gradle包裝成一個(gè)Gradle Task,這個(gè)TransForm Task會(huì)在java compile Task執(zhí)行完畢后運(yùn)行煮甥。

對(duì)于編寫(xiě)Transform的API, 我們可以通過(guò)引入下面這個(gè)依賴(lài)來(lái)使用:

compile 'com.android.tools.build:gradle:2.3.3'  //版本應(yīng)該在 2.x以上

先大致看一下Transform的執(zhí)行流程圖:

GradleTransform處理流程.png

Transform的使用場(chǎng)景

一般我們使用Transform會(huì)有下面兩種場(chǎng)景

  1. 我們需要對(duì)編譯class文件做自定義的處理盗温。
  2. 我們需要讀取編譯產(chǎn)生的class文件,做一些其他事情成肘,但是不需要修改它卖局。

接下來(lái)我們就來(lái)看一下這些Transform API吧 :

Transform API學(xué)習(xí)

我們編寫(xiě)一個(gè)自定義的transform需要繼承Transform,它是一個(gè)抽象類(lèi), 我們這里先看一下Transform的抽象方法:

public abstract class Transform {
    public abstract String getName();

    public abstract Set<ContentType> getInputTypes();

    public abstract Set<? super Scope> getScopes();

    public abstract boolean isIncremental(); // 是否支持增量編譯
}

getName()就是指定自定義的Transform的名字。

輸入的類(lèi)型

Set<ContentType> getInputTypes()是指明你自定義的這個(gè)Transform處理的輸入類(lèi)型双霍,輸入類(lèi)型共有以下幾種:

  enum DefaultContentType implements ContentType {
        /**
         * The content is compiled Java code. This can be in a Jar file or in a folder. If
         * in a folder, it is expected to in sub-folders matching package names.
         */
        CLASSES(0x01),

        /**
         * The content is standard Java resources.
         */
        RESOURCES(0x02);
    }

即分為class文件或者java資源砚偶。class文件來(lái)自于jar或者文件夾批销。資源就是標(biāo)準(zhǔn)的java資源。

輸入文件所屬的范圍 Scope

getScopes()用來(lái)指明自定的Transform的輸入文件所屬的范圍, 這是因?yàn)間radle是支持多工程編譯的蟹演》缱辏總共有以下幾種:

    /**
     * This indicates what the content represents, so that Transforms can apply to only part(s)
     * of the classes or resources that the build manipulates.
     */
    enum Scope implements ScopeType {
        /** Only the project content */
        PROJECT(0x01), //只是當(dāng)前工程的代碼
        /** Only the project's local dependencies (local jars) */
        PROJECT_LOCAL_DEPS(0x02), // 工程的本地jar
        /** Only the sub-projects. */
        SUB_PROJECTS(0x04),  // 只包含子工工程
        /** Only the sub-projects's local dependencies (local jars). */
        SUB_PROJECTS_LOCAL_DEPS(0x08),
        /** Only the external libraries */
        EXTERNAL_LIBRARIES(0x10),
        /** Code that is being tested by the current variant, including dependencies */
        TESTED_CODE(0x20),
        /** Local or remote dependencies that are provided-only */
        PROVIDED_ONLY(0x40);
    }

對(duì)于getScopes()的返回顷蟀,其實(shí)TransformManager已經(jīng)為我們定義了一些酒请,比如:

    public static final Set<Scope> SCOPE_FULL_PROJECT = Sets.immutableEnumSet(
            Scope.PROJECT, Scope.PROJECT_LOCAL_DEPS, Scope.SUB_PROJECTS, Scope.SUB_PROJECTS_LOCAL_DEPS, Scope.EXTERNAL_LIBRARIES);

如果一個(gè)Transform不想處理任何輸入,只是想查看輸入的內(nèi)容,那么只需在getScopes()返回一個(gè)空集合鸣个,在getReferencedScopes()返回想要接收的范圍羞反。

    public Set<? super Scope> getReferencedScopes() {
        return ImmutableSet.of();
    }

transform()

它是Transform的關(guān)鍵方法:

  public void transform(@NonNull TransformInvocation transformInvocation) {}

它是一個(gè)空實(shí)現(xiàn),input的內(nèi)容將會(huì)打包成一個(gè)TransformInvocation對(duì)象囤萤,因?yàn)槲覀円胧褂?code>input,我們需要詳細(xì)了解一下TransformInvocation參數(shù)昼窗。

TransformInvocation

我們看一下這個(gè)類(lèi)相關(guān)的API:

public interface TransformInvocation {

    Collection<TransformInput> getInputs(); // 輸入作為 TransformInput 返回

    TransformOutputProvider getOutputProvider(); //TransformOutputProvider 可以用來(lái)創(chuàng)建輸出內(nèi)容

    boolean isIncremental();
}

public interface TransformInput {
    Collection<JarInput> getJarInputs();
    Collection<DirectoryInput> getDirectoryInputs();
}

public interface JarInput extends QualifiedContent {

    File getFile(); //jar文件

    Set<ContentType> getContentTypes(); // 是class還是resource

    Set<? super Scope> getScopes();  //屬于Scope:
}

DirectoryInput與JarInput定義基本相同。

public interface TransformOutputProvider {
    //根據(jù) name涛舍、ContentType澄惊、QualifiedContent.Scope返回對(duì)應(yīng)的文件( jar / directory)
    File getContentLocation(String name, Set<QualifiedContent.ContentType> types, Set<? super QualifiedContent.Scope> scopes, Format format);
}

即我們可以通過(guò)TransformInvocation來(lái)獲取輸入,同時(shí)也獲得了輸出的功能。舉個(gè)例子富雅,

    public void transform(TransformInvocation invocation) {
        for (TransformInput input : invocation.getInputs()) {
            input.getJarInputs().parallelStream().forEach(jarInput -> {
            File src = jarInput.getFile();
            JarFile jarFile = new JarFile(file);
            Enumeration<JarEntry> entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry entry = entries.nextElement();
                //處理
            }
        }
    }

上面這段代碼就是獲取jar的輸入掸驱,然后遍歷每一個(gè)jar做一些自定義的處理。

我們?cè)谧鐾曜远x的處理后没佑,如果想自己輸出一些東西怎么辦毕贼? 比如一個(gè)class文件,就可以通過(guò)TransformOutputProvider來(lái)完成。比如下面這段代碼:

    File dest = invocation.getOutputProvider().getContentLocation("susion", TransformManager.CONTENT_CLASS, ImmutableSet.of(QualifiedContent.Scope.PROJECT), Format.DIRECTORY;

這段代碼就是在本工程(ImmutableSet.of(QualifiedContent.Scope.PROJECT))下產(chǎn)生一個(gè)目錄(Format.DIRECTORY), 目錄的名字是(susion),里面的內(nèi)容是TransformManager.CONTENT_CLASS蛤奢。

創(chuàng)建這個(gè)文件夾后鬼癣,我們就可以向其中寫(xiě)入一些內(nèi)容,比如class文件啤贩。

注冊(cè)Transform

我們?cè)诹私?code>transform api后待秃,我們可以編寫(xiě)一個(gè)自定義的Transform。但是我們編寫(xiě)的這個(gè)Transform,如何在構(gòu)建過(guò)程中生效呢痹屹?我們需要注冊(cè)它

在自定義插件中注冊(cè)它,然后在build.gradleapply就可以了锥余。

//MyCustomPlgin.groovy
public class MyCustomPlgin implements Plugin<Project> {

    @Override
    public void apply(Project project) {
        project.getExtensions().findByType(BaseExtension.class)
                .registerTransform(new MyCustomTransform());
    }
}

其實(shí)如果你包含了你編寫(xiě)的transform庫(kù),我們也可以直接在build.gradle中注冊(cè):

//在build.gradle中也是可以直接編寫(xiě) groovy代碼的痢掠。
project.extensions.findByType(BaseExtension.class).registerTransform(new MyCustomTransform());

End

好驱犹,有關(guān)transform的基本使用都已經(jīng)了解完畢。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末足画,一起剝皮案震驚了整個(gè)濱河市雄驹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌淹辞,老刑警劉巖医舆,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蔬将,警方通過(guò)查閱死者的電腦和手機(jī)爷速,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)霞怀,“玉大人惫东,你說(shuō)我怎么就攤上這事”惺” “怎么了廉沮?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)徐矩。 經(jīng)常有香客問(wèn)我滞时,道長(zhǎng),這世上最難降的妖魔是什么滤灯? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任坪稽,我火速辦了婚禮,結(jié)果婚禮上鳞骤,老公的妹妹穿的比我還像新娘窒百。我一直安慰自己,他們只是感情好弟孟,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布贝咙。 她就那樣靜靜地躺著,像睡著了一般拂募。 火紅的嫁衣襯著肌膚如雪庭猩。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 48,970評(píng)論 1 284
  • 那天陈症,我揣著相機(jī)與錄音蔼水,去河邊找鬼。 笑死录肯,一個(gè)胖子當(dāng)著我的面吹牛趴腋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播论咏,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼优炬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了厅贪?” 一聲冷哼從身側(cè)響起蠢护,我...
    開(kāi)封第一講書(shū)人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎养涮,沒(méi)想到半個(gè)月后葵硕,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體眉抬,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年懈凹,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蜀变。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡介评,死狀恐怖库北,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情威沫,我是刑警寧澤贤惯,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布洼专,位于F島的核電站棒掠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏屁商。R本人自食惡果不足惜烟很,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蜡镶。 院中可真熱鬧雾袱,春花似錦、人聲如沸官还。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)望伦。三九已至林说,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間屯伞,已是汗流浹背腿箩。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留劣摇,地道東北人珠移。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像末融,于是被迫代替她去往敵國(guó)和親钧惧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理勾习,服務(wù)發(fā)現(xiàn)浓瞪,斷路器,智...
    卡卡羅2017閱讀 134,599評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,498評(píng)論 25 707
  • 用兩張圖告訴你语卤,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料追逮? 從這篇文章中你...
    hw1212閱讀 12,693評(píng)論 2 59
  • 本文以com.android.tools.build:gradle:3.1.2 源碼來(lái)進(jìn)行分析酪刀,文章對(duì)應(yīng)的項(xiàng)目地址...
    sososeen09閱讀 27,720評(píng)論 9 63
  • nwt176 當(dāng)范冰冰發(fā)“武月很開(kāi)心”微博氣崔永元的時(shí)候骂倘,她沒(méi)想到等待自己的是現(xiàn)在的傳聞和麻煩。 張馨予可能沒(méi)想到...
    船長(zhǎng)戚戈XGeek001閱讀 100評(píng)論 0 0