适用于Android项目的Gradle插件,用来增量编译class和生成增量dex。跟自带的增量编译Task(assemble)的区别是:这个插件只会编译源文件,不做其他多余的动作

Overview

超快编译源文件的Gradle插件,支持Java和Kotlin


博客详情:敬请期待。。。


介绍:

插件源自WanAndroid每日一问:极致的编译优化如何实现?

插件提供了2个Task:incremental/incrementalCompile(增量编译源文件)、incremental/generateIncrementalDex(增量编译 + 生成增量dex);

每次只会编译有改动的源文件,增量编译过程中不依赖Android Gradle Plugin的任何一个Task(除了需要生成R文件和BuildConfig的时候);

与Android Gradle Plugin的assemble Task共享编译记录(它编译过的文件且无改动的话,插件Task不会再编译,插件编译过的文件无改动的话它也不会再次编译);

需要clone代码进行本地测试的同学,我在plugin/build.gradle.kts里面已把详细步骤说明,这里就不赘述了。


大致原理:

研究了assembleDebug的增量编译的大致原理(Java),发现它是通过JavaCompile的compile方法来完成对.java文件的编译的,这个方法有个叫inputChanges的参数,它描述了哪些文件在上一次编译后有改动,实现增量编译最重要就是这个参数了。

那它是怎么知道哪些文件有改动的呢?

是这样的,assembleDebug在每次编译完成之后,都会把本次参与编译的源文件的指纹(md5sum)记录在project/.gradle/gradle-version/executionHistory/executionHistory.bin文件里 。如果是第一次编译,那就是全部源文件了,后面每次只保存有变更的,因为没变更的不需要参与编译,它的md5也不会变。

指纹对比与直接对比修改日期有个优点就是,只要内容不变它的指纹都是不变的,而判断修改日期的话,可能那个文件刚开始有修改过,但在编译之前又撤销了修改,这样它的修改日期依然会变更。

插件的做法是:

初始化完成后直接拿到compileDebugJavaWithJavaccompileDebugKotlin(如果项目支持Kotlin的话)所对应的Compile(负责编译文件的类);

在进行一些基本的配置之后,使用跟assembleDebug相同的方式来计算出本次变更的文件,并交给Compile处理;

Compile编译完成后,把这些参与编译的文件记录到assembleDebug这个Task所使用的ExecutionHistoryStore中;

如果运行的是generateIncrementalDex,还会借助D8工具(D8是SDK 28之后才存在build-tools里,这就是为什么要求compileSdkVersion不低于28的原因)来把这些类打包到dex里面。


效果演示:

随意改动不同module的几个文件:

preview

通过generateIncrementalDex来生成增量dex:

preview

preview

Task执行完毕后,在project/build/outputs/merged_incremental_dex目录下会生成classes.dex

preview

直接打开,会发现刚刚2个module中有改动的3个类都在里面了:

preview


使用方式:

新版Gradle:

plugins {
    ...
    id "com.github.wuyr.incrementalcompiler" version "1.0.0"
}

旧版Gradle:

在项目下的build.gradle(注意不是module/build.gradle,是项目根目录下的build.gradle哦)加上maven地址和classpath,像这样:

buildscript {
    ...
    repositories {
        ...
        maven { url "https://plugins.gradle.org/m2/" }    
    }

    dependencies {
        ...
        classpath "com.github.wuyr.incrementalcompiler:plugin:1.0.0"
    }
}

如果上面的plugins.gradle.org/m2访问速度很慢,也可以换成国内的镜像地址,比如阿里云的:
maven { url 'https://maven.aliyun.com/nexus/content/repositories/gradle-plugin' }

然后在目标module里加上:

apply plugin: "com.github.wuyr.incrementalcompiler"

即可。


应用到所有module:

如果想为项目中所有module应用的话,可以在项目下的build.gradle(注意不是module/build.gradle,是项目根目录下的build.gradle哦)中直接遍历所有module,像这样:

subprojects {
    apply plugin: 'com.github.wuyr.incrementalcompiler'
}

要求:

  • Gradle Version不低于6.1.1

  • Android Gradle Plugin Version不低于3.6.0

  • compileSdkVersion/buildToolsVersion不低于28


更新日志:

  • 1.0.0 完成基本功能。

感谢:

感谢wanandroid交流群里的 "Faded"、"亦梦" 帮忙测试各Gradle版本兼容性。

You might also like...
Comments
  • Caching has not been enabled for the task

    Caching has not been enabled for the task

    Caching disabled for task ':xxxx:generateIncrementalDex' because: Caching has not been enabled for the task Task ':xxxx:generateIncrementalDex' is not up-to-date because: Task has not declared any outputs despite executing actions.

    跑到这就结束了 不会生成.dex文件 请问是什么原因呢

    opened by zyhGH 6
  • java.lang.NoSuchMethodException: findHighestSdkInstalled()

    java.lang.NoSuchMethodException: findHighestSdkInstalled()

    classpath 'com.android.tools.build:gradle:3.5.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20" classpath "com.github.wuyr.incrementalcompiler:plugin:1.0.0"

    image

    感觉好像已经编译成功了

    opened by yizems 1
Owner
陈小缘
陈小缘