Android Gradle 插件編寫指南

隨著Android的發(fā)展某抓,性能優(yōu)化铅祸、組件化等等不少開發(fā)工作不得不在編譯的過程中依賴對Java字節(jié)碼的修改奈应,這就要求我們學會基本的Gradle插件的實現(xiàn)。
但編寫Android Gradle插件開始會遇到兩個障礙贯涎,

  1. Groovy語言
  2. 官方Gradle插件的工作原理

第一個障礙很好解決,因為我們完全可以使用Java編寫慢洋,但是由于Gradle使用的Groovy塘雳,我們需要了解Groovy,以便閱讀其它插件的源碼普筹,所以針對這一點败明,我們可以不用,但是要看得懂太防。

可以過一遍語法特性:

https://www.ibm.com/developerworks/cn/education/java/j-groovy/j-groovy.html

第二個障礙不好解決妻顶,因為這方面的文檔確實很少,需要自己去閱讀源碼,API Source的文檔讳嘱。但是不要被它嚇到幔嗦,這個畢竟是工具,我們只要會用就夠了沥潭,所以我們看幾個關鍵部分邀泉,理解它的工作流程就足夠了。
拿到源碼最快的方式是添加這個依賴

implement 'com.android.tools.build:gradle:x.x.x'

然后我們找到TaskManager.java類叛氨,關注createPostCompilationTasks這個函數(shù)呼渣。

/**
     * Creates the post-compilation tasks for the given Variant.
     *
     * These tasks create the dex file from the .class files, plus optional intermediary steps like
     * proguard and jacoco
     */
    public void createPostCompilationTasks(
            @NonNull TaskFactory tasks,
            @NonNull final VariantScope variantScope) {

        ...

        // ----- External Transforms -----
        // apply all the external transforms.
        List<Transform> customTransforms = extension.getTransforms();
        List<List<Object>> customTransformsDependencies = extension.getTransformsDependencies();

        for (int i = 0, count = customTransforms.size(); i < count; i++) {
            Transform transform = customTransforms.get(i);

            List<Object> deps = customTransformsDependencies.get(i);
            transformManager
                    .addTransform(tasks, variantScope, transform)
                    .ifPresent(t -> {
                        if (!deps.isEmpty()) {
                            t.dependsOn(tasks, deps);
                        }

                        // if the task is a no-op then we make assemble task depend on it.
                        if (transform.getScopes().isEmpty()) {
                            variantScope.getAssembleTask().dependsOn(tasks, t);
                        }
                    });
        }

        ...
    }

首先我們需要知道,根據(jù)Android Moudule類型的不同寞埠,分為AppExtension和LibraryExtension屁置,源碼將編譯過程中的一個個流程封裝成若干個Transform,我們自定義插件就是為了在原來流程上添加我們自己的Transform仁连。
例如蓝角,我在Application編譯流程上添加一個流程,

    public class MyPlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
        AppExtension appExtension = (AppExtension) project.getExtensions().getByName("android");
        appExtension.registerTransform(new CustomTransform(project), Collections.EMPTY_LIST);
    }
}

根據(jù)上述源碼可知饭冬,自定義的Transform的工作位置在生成dex之前使鹅,生成class文件之后,這樣昌抠,我們就可以在Transform里實現(xiàn)我們的字節(jié)碼修改了患朱。

public class CustomTransform extends Transform {

    private Logger logger;

    private static final FileTime ZERO = FileTime.fromMillis(0);

    public CustomTransform(Project project) {
        logger = project.getLogger();
    }

    @Override
    public String getName() {
        return "CustomTransform";
    }

    @Override
    public Set<QualifiedContent.ContentType> getInputTypes() {
        return TransformManager.CONTENT_CLASS;
    }

    @Override
    public Set<? super QualifiedContent.Scope> getScopes() {
        return TransformManager.SCOPE_FULL_PROJECT;
    }

    @Override
    public boolean isIncremental() {
        return true;
    }

    @Override
    public Set<QualifiedContent.ContentType> getOutputTypes() {
        return super.getOutputTypes();
    }

    @Override
    public Set<? super QualifiedContent.Scope> getReferencedScopes() {
        return TransformManager.EMPTY_SCOPES;
    }

    @Override
    public Map<String, Object> getParameterInputs() {
        return super.getParameterInputs();
    }

    @Override
    public boolean isCacheable() {
        return true;
    }

    @Override
    public void transform(TransformInvocation transformInvocation) throws TransformException, InterruptedException, IOException {
        super.transform(transformInvocation);
        //自定義工作
    }
}

在創(chuàng)建我們自己的Transform的時候,需要選擇我們的目標文件的類型和范圍炊苫,比如例子中我需要處理整個項目中的class文件裁厅,設置輸入文件類型為TransformManager.CONTENT_CLASS,設置輸入文件范圍為TransformManager.SCOPE_FULL_PROJECT侨艾,這樣我們就能通過transform()中的TransformInvocation.getInputs()攔截到需要處理的文件执虹。

我們代碼源文件生成的class文件在對應的目錄下,依賴庫的class文件在jar包里唠梨,所以TransformInput又分為JarInputs和DirectoryInput袋励,需要分別處理:

for (TransformInput input : inputs) {
    for (JarInput jarInput : input.getJarInputs()) {
        //處理jar文件
    }
    for (DirectoryInput directoryInput : input.getDirectoryInputs()) {
        //處理目錄下的class文件
    }
}

完成了插件的編寫,最后一步是發(fā)布插件当叭,參考Gradle官方文檔:

https://docs.gradle.org/current/userguide/publishing_overview.html

我們需要在build.gradle里添加發(fā)布的參數(shù)

apply plugin: 'maven-publish'

publishing {
    publications {
        myLibrary(MavenPublication) {
            from components.java
            groupId = "dong"
            artifactId = "myplugin"
            version = 1.0
        }
    }

    repositories {
        maven {
            name = 'myRepo'
            url = "../repo"
        }
    }
}

gradle會自動幫我們生成一系列的發(fā)布Task


gradle_publish_task.png

按需使用就行了,例如:

buildscript {
    repositories {
        maven {
            url uri("/repo")
        }
        jcenter()
        google()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.4.2'
        classpath 'dong:myplugin:1.0'
    }
}

apply plugin: 'myplugin'

附簡單示例代碼:

https://github.com/jpdong/ASMPluginSample

愿大家玩得開心茬故。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市科展,隨后出現(xiàn)的幾起案子均牢,更是在濱河造成了極大的恐慌,老刑警劉巖才睹,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件徘跪,死亡現(xiàn)場離奇詭異甘邀,居然都是意外死亡,警方通過查閱死者的電腦和手機垮庐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門松邪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人哨查,你說我怎么就攤上這事逗抑。” “怎么了寒亥?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵邮府,是天一觀的道長。 經(jīng)常有香客問我溉奕,道長褂傀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上雄右,老公的妹妹穿的比我還像新娘。我一直安慰自己叠国,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布戴尸。 她就那樣靜靜地躺著粟焊,像睡著了一般。 火紅的嫁衣襯著肌膚如雪孙蒙。 梳的紋絲不亂的頭發(fā)上吆玖,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音马篮,去河邊找鬼。 笑死怜奖,一個胖子當著我的面吹牛浑测,可吹牛的內容都是我干的。 我是一名探鬼主播歪玲,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼迁央,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了滥崩?” 一聲冷哼從身側響起岖圈,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钙皮,沒想到半個月后蜂科,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體顽决,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年导匣,在試婚紗的時候發(fā)現(xiàn)自己被綠了才菠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡贡定,死狀恐怖赋访,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情缓待,我是刑警寧澤蚓耽,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站旋炒,受9級特大地震影響步悠,放射性物質發(fā)生泄漏。R本人自食惡果不足惜国葬,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一贤徒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧汇四,春花似錦接奈、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至背苦,卻和暖如春互捌,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背行剂。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工秕噪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人厚宰。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓腌巾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親铲觉。 傳聞我的和親對象是個殘疾皇子澈蝙,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內容