Android/Java 开发中的常见需求:在编译时将 Gradle 中定义的值注入到 Java 代码中 ,那么如何通过 Gradle 脚本声明/更改 Java 变量
buildConfigField(最常用)
在 build.gradle 中定义字段,编译后自动生成到 BuildConfig 类中。
android {
defaultConfig {
// 类型、字段名、值
buildConfigField "String", "API_BASE_URL", '"https://api.example.com"'
buildConfigField "boolean", "ENABLE_LOGGING", "true"
buildConfigField "int", "MAX_RETRY", "3"
buildConfigField "long", "BUILD_TIME", "${System.currentTimeMillis()}L"
}
// 也可以按 flavor / buildType 分别定义
buildTypes {
debug {
buildConfigField "String", "API_BASE_URL", '"https://debug.api.example.com"'
buildConfigField "boolean", "ENABLE_LOGGING", "true"
}
release {
buildConfigField "String", "API_BASE_URL", '"https://api.example.com"'
buildConfigField "boolean", "ENABLE_LOGGING", "false"
}
}
}
Java 端使用
// 自动生成的 BuildConfig 类,直接引用
String url = BuildConfig.API_BASE_URL;
boolean logging = BuildConfig.ENABLE_LOGGING;
int retry = BuildConfig.MAX_RETRY;
编译后会在
build/generated/source/buildConfig/.../BuildConfig.java自动生成对应字段。
通过 gradle.properties + BuildConfig
将值集中管理在属性文件中,Gradle 读取后注入。
gradle.properties
API_BASE_URL=https://api.example.com
ENABLE_LOGGING=true
MAX_RETRY=3
build.gradle
// 读取属性(注意类型转换)
buildConfigField "String", "API_BASE_URL", "\"${project.properties['API_BASE_URL']}\""
buildConfigField "boolean", "ENABLE_LOGGING", project.properties['ENABLE_LOGGING']
buildConfigField "int", "MAX_RETRY", project.properties['MAX_RETRY']
自动生成 Java 源文件
当需要的变量较多、或者不想依赖 BuildConfig 时,可以直接生成一个 .java 文件。
android {
applicationVariants.all { variant ->
def task = variant.registerJavaGeneratingTask(
"generate${variant.name.capitalize()}Config",
file("${buildDir}/generated/source/config/${variant.dirName}")
)
task.get().doLast {
def outputDir = file("${buildDir}/generated/source/config/${variant.dirName}/com/example")
outputDir.mkdirs()
def configFile = new File(outputDir, "AppConfig.java")
configFile.text = """
package com.example;
public final class AppConfig {
public static final String API_BASE_URL = "https://api.example.com";
public static final boolean ENABLE_LOGGING = ${variant.buildType.isDebuggable()};
public static final int MAX_RETRY = 3;
}
""".stripIndent()
}
}
}
Java 端直接用:
String url = AppConfig.API_BASE_URL;
Manifest 占位符(Android Manifest 专用)
用于向 AndroidManifest.xml 注入变量。
android {
defaultConfig {
manifestPlaceholders = [
APP_NAME: "MyApp Debug",
CHANNEL: "google_play"
]
}
}
<!-- AndroidManifest.xml -->
<application
android:label="${APP_NAME}"
android:name=".MyApp">
<meta-data
android:name="CHANNEL"
android:value="${CHANNEL}" />
</application>
系统属性(主要用于测试)
test {
systemProperty "api.url", "https://test.api.example.com"
systemProperty "thread.count", "8"
}
// 测试代码中
String apiUrl = System.getProperty("api.url");
各方案对比
| 方案 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
buildConfigField |
常量配置、环境区分 | 最简单,官方支持 | 仅限 Android,字段生成在 BuildConfig 中 |
gradle.properties + BuildConfig |
集中管理配置 | 配置与代码分离 | 本质仍是方案一的包装 |
| 生成 Java 源文件 | 大量配置 / 非 Android 项目 | 完全自定义 | 实现稍复杂 |
| Manifest 占位符 | AndroidManifest 动态值 | 直接替换 XML | 仅限 Manifest |
systemProperty |
单元测试 | 简单直接 | 仅限运行时 |
常见注意事项
字符串引号陷阱:
// 错误 --- Java 端会缺少引号,编译报错
buildConfigField "String", "URL", "https://example.com"
// 正误 --- 值本身要包含 Java 字符串的双引号
buildConfigField "String", "URL", '"https://example.com"'
// 或
buildConfigField "String", "URL", "\"https://example.com\""
敏感信息不要硬编码:
android {
defaultConfig {
buildConfigField("String", "API_BASE_URL", "\"https://api.example.com\"")
buildConfigField("boolean", "ENABLE_LOGGING", "true")
}
}
Kotlin DSL(build.gradle.kts)写法:
android {
defaultConfig {
buildConfigField("String", "API_BASE_URL", "\"https://api.example.com\"")
buildConfigField("boolean", "ENABLE_LOGGING", "true")
}
}
其他完整事例:
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
compileSdk 31
ArrayList src_lines
File versionPropsFile
defaultConfig {
applicationId "com.example.myapplication"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
//com.example.myapplication java ,kt 代码
versionPropsFile = file('./src/main/java/com/example/myapplication/MainActivity.kt')
if (versionPropsFile.canRead()) {
def stream = new BufferedReader(new InputStreamReader(new FileInputStream(versionPropsFile), "UTF-8"))
def lines = stream.readLines()
src_lines = new ArrayList<String>()
src_lines.addAll(lines)
stream.close()
for (int i = 0; i < lines.size(); i++) {
def line = lines.get(i)
// 替换java中 包含 "1234"的行
if (line.contains("1234")) {
lines.set(i, "val i = 4567")
}
// 替换java中 包含 "我是未修改过的文字或代码"的行
if (line.contains("我是未修改过的文字或代码")) {
lines.set(i, " return \"我已被Gradle动态修改了...而且int值改成了\"+i")
}
}
def writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(versionPropsFile), "UTF-8"))
for (def line : lines) {
writer.writeLine(line)
println(line)
}
writer.flush()
writer.close()
// new Thread(new Runnable() {
// @Override
// void run() {
// Thread.sleep(1000)
// def writer2 = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(versionPropsFile), "UTF-8"))
// for (def line : src_lines) {
// writer2.writeLine(line)
// }
// writer2.flush()
// writer2.close()
// }
// }).start()
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
viewBinding true
}
}
task task7() {
modifyBuildTime()
}
def modifyBuildTime() {
// res中json文件 [ { "id": "1", "courseID": "1640850159135", "title": "一加一等于几" }, { "id": "2", "courseID": "1640850159135", "title": "请背诵全文" } ]
def path = "app\\src\\main\\res\\raw\\test.json"
def time = new Date().getTime()
println("buildtime =========== " + time)
def keyword = "courseID"
def newString = " \"courseID\": \"" + time + "\","
def content = fileReader(path, keyword, newString)
fileWrite(path, content)
}
//读取文件并替换字符串
def fileReader(path, keyword, newString) {
def readerString = "";
new File(path).withReader('UTF-8') { reader ->
reader.eachLine {
if (it.contains(keyword)) {
it = it.replace(it, newString)
}
readerString <<= it
readerString << '\n'
}
return readerString
}
}
//写入文件
def fileWrite(path, content) {
new File(path).withWriter('UTF-8') { writer -> writer.append(content) }
}