Skip to the content.

Gradle动态编译能力


Android的Gradle插件内置了一些轻量且便捷的动态编译能力,不用写复杂的脚本也能在编译期生成动态变化的内容。

本文内容原写于2019年,如文中内容如今已有变化,劳烦告知。

动态生成Java常量

// build.gradle

def timestamp(format = 'yyyyMMdd_HHmmss') {
    new java.text.SimpleDateFormat(format).format(new Date())
}

android {
	//...
    buildTypes {
        all {
            buildConfigField 'int', 'BUILD_NUMBER', "${System.getenv('build_number').toInteger()}"
            buildConfigField 'boolean', 'FEATURE_ENABLED', "${System.getenv('feature_enabled').toBoolean()}"
            buildConfigField 'String', 'BUILD_TIME', "\"${timestamp()}\"" // 注意引号
        }
    }
}
// build/generated/source/buildConfig/debug/com/example/BuildConfig.java
package com.example;
public final class BuildConfig {
  //...
  public static final int BUILD_NUMBER = 1;
  public static final boolean FEATURE_ENABLED = true;
  public static final String BUILD_TIME = "20190411_195418";
}

实际应用中,如果增强一下脚本甚至可以生成Map这样的复杂数据结构。

在普通的Java Application中也可以使用BuildConfig,只需要引入一个独立的Gradle插件:

buildscript {
    repositories {
        //...
        maven { url "https://plugins.gradle.org/m2/" }
    }
    dependencies {
        //...
        classpath "com.github.gmazzo:gradle-buildconfig-plugin:1.6.2"
    }
    //...
}

apply plugin: "com.github.gmazzo.buildconfig"

buildConfig {
    packageName 'com.example'
    buildConfigField 'String', 'LIBRARY_VERSION', "\"${BUILD_VERSION}\""
}

动态生成资源

// build.gradle
android {
	//...
    buildTypes {
        debug {
            resValue "string", "example_resource", "only use for test: ${new SimpleDateFormat("yyyyMMddHHmm").format(new Date())}"
        }
    }
}
<!-- 编译后的strings.xml -->
<string name="example_resource">only use for test: 201904111949</string>

动态修改AndroidManifest.xml

// build.gradle
android {
	//...
    buildTypes {
        all {
            manifestPlaceholders = [BUILD_TIME_STUB: new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date())]
        }
    }
}
<!-- AndroidManifest.xml 编译前 -->
<meta-data  android:name="BUILD_TIME" android:value="${BUILD_TIME_STUB}" tools:replace="android:value"/>
<!-- AndroidManifest.xml 编译后 -->
<meta-data android:name="BUILD_TIME" android:value="20190411_195418" />

另外,对AndroidManifest.xml而言applicationId是内置的,可以直接使用:

<!-- AndroidManifest.xml -->
<provider
    android:exported="true"
    android:name="com.example.MyContentProvider"
    android:authorities="${applicationId}.myprovider"
    android:process=":daemon" />

动态生成assets

afterEvaluate {
    android.applicationVariants.all { variant ->
        variant.mergeAssetsProvider.get().with {
            setOnlyIf { true }
            outputs.upToDateWhen { false }
            doLast {
                copy {
                    from sourceFile
                    into outputs.files.files.grep({
                        !it.absolutePath.contains('incremental')
                    }).first()
                    // like: build/intermetiates/assets/debug/mergeDebugAssets
                }
            }
        }
    }
}

通过上述脚本,可以把sourceFile打包到apk的assets目录下。这段脚本在Gradle-7.2和AGP-7.1.3环境下测试通过。