通过Gradle脚本声明更改Java变量

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) }
}
相关推荐
小喻同学i1 小时前
std::thread函数参数传递
开发语言·c++
程序员天天困2 小时前
后端视角看 EventBus:发布订阅总线的原理、场景与用法
java·后端
要开心吖ZSH2 小时前
AI医疗分诊与健康咨询助手agent开发——(4-2)从零到一:我给AI医疗分诊助手加了个“知识库大脑“,终于搞懂了RAG是什么
java·docker·agent·医疗·rag
智能体与具身智能2 小时前
TVA 本质内涵与核心特征(系列)
人工智能·python·智能体视觉
云雾J视界2 小时前
SST:高频变压器设计实战:铁氧体 vs 纳米晶 vs 非晶,磁性材料怎么选
python·acdc·dab·sst
第一程序员2 小时前
Rust trait 入门:把 AI 客户端抽象成可替换接口
python·rust·github
Ulyanov3 小时前
Python实现6-DOF刚体仿真器(下)——环境扰动与控制闭环
开发语言·python·算法·系统仿真·雷达电子对抗·导引头
分布式存储与RustFS3 小时前
RustFS Beta.10 性能解读:PUT 全面反超 MinIO,Rust 重写对象存储成了?
开发语言·后端·rust
小小的木头人3 小时前
Python 批量解析 Excel 经纬度,调用高德地图 API 获取中文地址
开发语言·python·excel