android studio之build.gradle写法简介

本文介绍如何在Android Studio中手动编辑build.gradle文件来添加JAR依赖、配置网络依赖及处理本地代码编译等问题。具体包括批量添加libs目录下的JAR包、引入网络上的依赖库、设置NDK编译选项等内容。

很多时候,studio的IDE并不能满足需求,在以下场景下,我们需要手动修改build.gradle文件来改变配置


为工程添加libs目录下所有JAR依赖

在module settings中为module增加dependence时,有如下选项:


第一项可以添加网络依赖的support jar包,第二项可以增加单个jar的依赖,第三个可以增加module依赖。

libs目录下有成吨的jar,每次添加都要修改配置,万一忘记就不好了。

可以在dependencies节点添加如下内容:

    compile fileTree(dir: 'src/main/libs', include: ['*.jar'])
用IDE添加单个JAR依赖后,build.gradle文件增加的对应的内容是“compile file”

增加网络JAR依赖

support-v4等包比较神烦,为了统一管理,可以直接引用。

不过好像直接用IDE也能修改,在dependencies节点下面增加:

    compile 'com.android.support:support-v4:19.1.0'

这段也是在depenencies下面,可以和上一段进行合并。。


修改了libs目录

修改了libs目录,也要修改配置,在android节点下增加如下内容:
sourceSets.main {
    jniLibs.srcDirs 'src/main/libs'
}

在NDK中引用了一些库

我的jni层使用的是C++代码,C++和C的JNI函数还是有区别的。

我的工程中使用了android的log以及C++的STL库。

之前是eclipse的工程,在C++代码中包含了:

#include <android/log.h>
而在jni的Application.mk文件中添加了STL库支持:
APP_STL := stlport_static
直接在命令行中NDK-BUILD没有错误,导出到studio之后编译找不到log以及STL。

现在在build.gradle文件的android.defaultConfig节点下加入如下内容:

ndk {
    moduleName "aa"
    stl "stlport_static"
    ldLibs "log"
}
其中aa是我的so库的名字。


最后,我的配置文件粘贴如下供参考:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 19
    buildToolsVersion "23.0.3"

    defaultConfig {
        applicationId "com.example.sample"
        minSdkVersion 14
        targetSdkVersion 19

        ndk {
            moduleName "aa"
            stl "stlport_static"
            ldLibs "log"
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }

    sourceSets.main {
        jniLibs.srcDirs 'src/main/libs'
    }
}

dependencies {
    compile 'com.android.support:support-v4:19.1.0'
    compile fileTree(dir: 'src/main/libs', include: ['*.jar'])
}


在android stduio编译时不重新编译native代码

这点很关键。每次编译的时候都编译一遍native层的代码,你是不是已经够了,这也是很多人说studio慢的另一个原因(第一个原因参考Gradle Build速度加快终极方法)。

编译时禁用native编译,在sourceSets.main下面加入:

jni.srcDirs = [] //disable automatic ndk-build call


<think>我们正在使用Kotlin DSL(build.gradle.kts)来配置Android项目。 目标:通过productFlavors自定义输出APK文件名,并在文件名中包含时间戳等信息。 步骤: 1.android块中配置productFlavors。 2. 为每个变体(variant)自定义输出文件名。 3. 在文件名中添加时间戳。 注意:在Kotlin DSL中,我们使用`applicationVariants.all`来遍历所有变体,并设置输出文件名。 具体实现: 我们将使用以下方式: - 获取当前时间戳,格式化为字符串(例如:yyyyMMddHHmm) - 在输出文件名中插入时间戳和版本信息 示例代码: android { ... buildTypes { release { isMinifyEnabled = false proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") } } flavorDimensions.add("version") productFlavors { create("free") { dimension = "version" applicationIdSuffix = ".free" versionNameSuffix = "-free" } create("pro") { dimension = "version" applicationIdSuffix = ".pro" versionNameSuffix = "-pro" } } // 配置所有变体 applicationVariants.all { // 生成时间戳 val timestamp = SimpleDateFormat("yyyyMMdd_HHmm", Locale.getDefault()).format(Date()) // 获取版本名称 val versionName = versionName // 获取构建类型 val buildType = buildType.name // 获取当前变体名称(例如:freeRelease) val variantName = name outputs.all { // 检查是否是Apk输出 if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) { // 设置输出文件名格式 outputFileName = "app_${variantName}_v${versionName}_${timestamp}.apk" } } } } 注意:在Kotlin DSL中,我们使用`applicationVariants.all`并遍历每个变体,然后对每个变体的输出进行操作。 但是,上述代码中直接使用`versionName`可能会遇到问题,因为变体的版本名称可能还未生成。我们可以通过变体的mergedFlavor来获取版本名称。 修正:我们可以使用`this@applicationVariants`来获取变体的版本信息。但是,在Kotlin DSL中,我们可以这样获取版本名称: val versionName = this.variantData.variantConfiguration.versionName 然而,这种方式并不稳定,因为variantData是内部API。更安全的方式是使用`mergedFlavor`: val version = mergedFlavor.versionName ?: android.defaultConfig.versionName 但是,在Kotlin DSL中,我们也可以这样: val versionName = versionName 实际上,在applicationVariants的上下文中,`versionName`属性是可用的,它表示最终合并的版本名称。 另外,我们还需要注意:在配置阶段,我们可能无法获取到所有的属性,因此我们可以在输出配置阶段设置文件名。 但是,上面的代码在`outputs.all`中设置文件名,这是正确的。 然而,在Kotlin DSL中,我们可能无法直接访问`com.android.build.gradle.internal.api.ApkVariantOutputImpl`,因为它是一个内部类。我们可以通过判断输出文件的后缀名来避免: outputs.all { val output = this // 只处理apk输出 if (output is com.android.build.gradle.api.ApkOutput) { // 设置文件名 output.outputFileName = "app_${variantName}_v${versionName}_${timestamp}.apk" } } 但是,在较新的Android Gradle Plugin中,我们通常使用以下方式: this.outputs.forEach { output -> if (output is com.android.build.gradle.internal.api.BaseVariantOutputImpl) { output.outputFileName = ... } } 但是,在Kotlin DSL中,我们使用`outputs.all`并直接设置`outputFileName`,因为每个输出都可能是多个(例如多APK输出)。但是,我们通常只有一个输出。 考虑到兼容性,我们可以这样: val variant = this variant.outputs.all { // 获取时间戳 val timestamp = SimpleDateFormat("yyyyMMdd_HHmm", Locale.getDefault()).format(Date()) // 使用变体的版本名称 val versionName = variant.versionName val buildType = variant.buildType.name val flavorName = variant.flavorName // 注意:如果多个维度,这里可能不准确 // 实际上,variant.name已经包含了flavor和buildType,例如freeRelease val fileName = "app_${variant.name}_v${versionName}_${timestamp}.apk" // 设置文件名 if (this is com.android.build.gradle.api.ApkOutput) { outputFileName = fileName } } 但是,`variant.flavorName`在多个flavor维度下可能不准确,所以我们使用`variant.name`。 但是,在Kotlin DSL中,`applicationVariants`中的每个变体(variant)的类型是`ApplicationVariant`,它有一个`name`属性,我们可以使用。 但是,注意:`outputs.all`中的闭包是在配置阶段执行的,而时间戳会在每次配置时生成。这可能导致每次构建都使用同一个时间戳(因为配置只执行一次),除非我们使用`doFirst`在任务执行时生成时间戳。但这样会导致配置阶段和执行阶段混合,可能会出现问题。 更常见的做法是在任务执行时修改文件名。我们可以使用Gradle的生命周期钩子,在任务执行阶段动态设置文件名。 但是,在Android Gradle Plugin中,我们可以在`applicationVariants.all`中设置输出文件名,并且每次构建都会重新计算,因为构建任务会重新配置。所以,每次构建都会生成新的时间戳,这是符合预期的。 但是,如果我们在同一个构建中多次调用配置(例如多项目构建),可能会多次执行。因此,我们确保在配置变体时设置输出文件名。 另外,注意:时间戳是在配置阶段生成的,所以如果我们连续运行两次构建,而不重新配置,那么时间戳不会更新。但是,通常每次构建都会重新配置整个项目。 因此,我们可以采用以下代码: android { ... applicationVariants.all { val variant = this val variantName = variant.name val versionName = variant.versionName val timestamp = SimpleDateFormat("yyyyMMdd_HHmm", Locale.getDefault()).format(Date()) variant.outputs.all { // 设置输出文件名 outputFileName = "app_${variantName}_v${versionName}_${timestamp}.apk" } } } 但是,这样会有一个问题:同一个变体的多个输出(如果有)会使用相同的时间戳,这可能是期望的。 但是,上面的代码中,每个变体的所有输出都会使用同一个时间戳,这没问题。 然而,实际测试发现,在配置阶段获取的时间戳是固定的,即使多次构建,只要在同一个Gradle daemon进程中,配置阶段只执行一次。因此,如果我们连续构建两次,第二次构建会使用第一次构建时生成的时间戳。 为了解决这个问题,我们应该在任务执行阶段生成时间戳。我们可以通过将时间戳的生成放在一个`doFirst`中,但是这样我们就不能直接设置`outputFileName`了,因为配置阶段已经结束。 所以,我们需要在输出配置中使用一个技巧:将设置文件名的代码延迟到任务执行时。但是,这并不容易。 另一种做法:在配置阶段使用当前时间,但是每次构建都重新配置。我们可以通过禁用配置缓存来保证每次构建都重新配置,但这会降低构建速度。 推荐做法:在任务执行时修改输出文件名。我们可以通过注册一个任务动作来实现。 具体步骤: android.applicationVariants.all { variant -> // 获取变体对应的assemble任务 val assembleTask = variant.assembleProvider.get() // 在任务执行前修改输出文件名 assembleTask.doFirst { variant.outputs.forEach { output -> // 重新生成时间戳 val timestamp = SimpleDateFormat("yyyyMMdd_HHmm", Locale.getDefault()).format(Date()) val versionName = variant.versionName val fileName = "app_${variant.name}_v${versionName}_${timestamp}.apk" // 修改输出文件名 output.outputFileName = fileName } } } 但是,这种方法在Kotlin DSL中可能无法实现,因为`outputFileName`在配置阶段之后可能无法修改。 实际上,在Android Gradle Plugin 3.0之后,我们可以在配置阶段设置输出文件名,并且每次构建都会重新配置。因此,如果我们每次构建都重新运行配置阶段(即不使用配置缓存),那么时间戳每次都会更新。但是,Gradle的配置缓存可能会缓存配置阶段,导致时间戳不变。 因此,我们需要禁用配置缓存,或者使用其他方式。 考虑到简单性,我们假设用户不使用配置缓存(默认不启用)。所以,我们可以使用配置阶段的时间戳。 但是,为了确保每次构建都生成新的时间戳,我们可以在配置阶段读取当前时间。在大多数情况下,每次构建都会重新运行配置阶段,所以时间戳会更新。 因此,我们回到最初的方案: android { // ... applicationVariants.all { val variant = this val timestamp = SimpleDateFormat("yyyyMMdd_HHmm", Locale.getDefault()).format(Date()) variant.outputs.all { outputFileName = "app_${variant.name}_v${variant.versionName}_${timestamp}.apk" } } } 但是,注意:`variant.versionName`可能为null,我们可以使用安全操作符。 另外,我们可能希望时间戳只包含日期,或者使用其他格式,可以调整SimpleDateFormat。 另外,如果变体有多个输出,我们可能需要为每个输出设置不同的文件名?通常,我们只有一个输出(一个APK)。 因此,我们这样android { // ... applicationVariants.all { val variant = this // 格式化时间戳,例如:20240530_1430 val timestamp = SimpleDateFormat("yyyyMMdd_HHmm", Locale.getDefault()).format(Date()) // 使用变体的名称和版本号 val variantName = variant.name val versionName = variant.versionName ?: "unknown" // 为每个输出设置文件名 variant.outputs.all { // 设置文件名 outputFileName = "app_${variantName}_v${versionName}_${timestamp}.apk" } } } 注意:在Kotlin DSL中,`SimpleDateFormat`需要导入,所以我们在文件顶部添加: import java.text.SimpleDateFormat import java.util.* 另外,我们还需要注意:文件名中不能包含一些特殊字符,如冒号,所以我们使用下划线。 但是,如果同一个变体在同一个时间戳内多次构建(例如在同一个分钟内),那么文件名会冲突。为了避免这种情况,我们可以将时间戳精确到秒,或者加上随机字符串?但是,通常构建不会那么频繁。 因此,我们使用精确到分钟的时间戳已经足够。如果需要更精确,可以改为`yyyyMMdd_HHmmss`。 最后,我们还需要考虑:不同的构建类型(debug/release)和不同的flavor,所以每个变体都会有自己的文件名。 示例输出:app_freeRelease_v1.0.0_20240530_1430.apk 完整示例(build.gradle.kts): import com.android.build.gradle.internal.api.BaseVariantOutputImpl import java.text.SimpleDateFormat import java.util.* android { ... buildTypes { getByName("release") { isMinifyEnabled = false proguardFiles(getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro") } } flavorDimensions("version") productFlavors { create("free") { dimension = "version" applicationIdSuffix = ".free" versionNameSuffix = "-free" } create("pro") { dimension = "version" applicationIdSuffix = ".pro" versionNameSuffix = "-pro" } } applicationVariants.all { val variant = this val timestamp = SimpleDateFormat("yyyyMMdd_HHmm", Locale.getDefault()).format(Date()) val variantName = variant.name val versionName = variant.versionName variant.outputs.all { // 设置输出文件名 if (this is BaseVariantOutputImpl) { outputFileName = "app_${variantName}_v${versionName}_${timestamp}.apk" } } } } 注意:在Kotlin DSL中,我们使用`all`来遍历所有变体,然后对每个变体的所有输出进行操作。 但是,在Kotlin中,我们需要导入`BaseVariantOutputImpl`,因为它是内部API,所以可能会被标记为`@Internal`。在较新的Android Gradle Plugin中,可能无法访问。因此,我们可以使用反射或者避免类型检查。 实际上,我们可以不使用类型检查,直接设置`outputFileName`,因为`outputs`中的每个输出都有这个属性(在Kotlin DSL中,`com.android.build.gradle.api.BaseVariantOutput`有`outputFileName`属性)。 所以,我们可以简化为: variant.outputs.all { // 直接设置outputFileName,因为all方法中的每个输出都有这个属性 outputFileName = "app_${variantName}_v${versionName}_${timestamp}.apk" } 但是,在Kotlin中,`outputFileName`是`com.android.build.gradle.api.BaseVariantOutput`的属性,所以我们可以直接使用。 因此,我们不需要导入内部类,也不需要类型检查。 最终代码: import java.text.SimpleDateFormat import java.util.* android { // ... 其他配置 applicationVariants.all { val variant = this val timestamp = SimpleDateFormat("yyyyMMdd_HHmm", Locale.getDefault()).format(Date()) val variantName = variant.name val versionName = variant.versionName variant.outputs.all { outputFileName = "app_${variantName}_v${versionName}_${timestamp}.apk" } } } 注意:如果版本名中包含特殊字符(如空格),我们需要替换掉,因为文件名不支持空格。我们可以使用: val safeVersionName = versionName?.replace(" ", "_") ?: "unknown" 所以,最终代码为: variant.outputs.all { val safeVersionName = versionName?.replace(" ", "_") ?: "unknown" outputFileName = "app_${variantName}_v${safeVersionName}_${timestamp}.apk" } 这样,我们就实现了在Kotlin DSL中为productFlavors自定义输出APK文件名,并包含时间戳。 测试:在Android Studio中执行构建,查看输出的APK文件名是否符合预期。 相关问题: 1. 如何在Kotlin DSL中配置productFlavors? 2. 如何为不同的构建类型(debug/release)设置不同的输出文件名? 3. 如何在Android Gradle Plugin 7.0+中自定义输出文件名? 4. 如何在输出文件名中添加Git提交ID?</think>在 Android Studio 中使用 Kotlin DSL (`build.gradle.kts`) 为 `productFlavors` 自定义 APK 文件名,可通过以下步骤实现: ### 解决方案(Kotlin DSL 配置) ```kotlin import com.android.build.api.variant.ApplicationAndroidComponentsExtension import java.text.SimpleDateFormat import java.util.* plugins { id("com.android.application") } android { compileSdk = 34 buildToolsVersion = "34.0.0" defaultConfig { versionName = "1.0.0" versionCode = 1 } // 1. 定义维度 flavorDimensions += "environment" // 2. 配置 productFlavors productFlavors { create("dev") { dimension = "environment" applicationIdSuffix = ".dev" versionNameSuffix = "-dev" } create("prod") { dimension = "environment" applicationIdSuffix = ".prod" } } // 3. 使用 setProperty 配置文件名(关键步骤) configure<ApplicationAndroidComponentsExtension> { onVariants { variant -> // 生成时间戳 (格式: yyyyMMdd_HHmm) val timestamp = SimpleDateFormat("yyyyMMdd_HHmm", Locale.getDefault()).format(Date()) // 获取版本信息 val versionName = variant.versionName ?: "unknown" // 4. 自定义输出文件名 variant.outputs.forEach { output -> output.outputFileName.set( "app_${variant.name}_v${versionName}_${timestamp}.apk" ) } } } } ``` ### 关键点说明 1. **文件名格式解析** `app_${variant.name}_v${versionName}_${timestamp}.apk` 示例输出: `app_devDebug_v1.0.0-dev_202405301430.apk` 2. **时间戳生成** ```kotlin SimpleDateFormat("yyyyMMdd_HHmm", Locale.getDefault()).format(Date()) ``` - `yyyy`:四位年份(如 2024) - `MM`:两位月份(01-12) - `dd`:两位日期(01-31) - `HH`:24小时制小时(00-23) - `mm`:分钟(00-59) 3. **动态属性获取** - `variant.name`:组合 flavor + buildType(如 `devDebug`) - `variant.versionName`:从 `defaultConfig` 和 `productFlavors` 合并的版本名 4. **文件命名规则** - 使用 `set()` 方法动态设置文件名 - 文件名中**避免空格和特殊字符**(用下划线 `_` 替代) ### 注意事项 1. **兼容性** 此配置要求 Android Gradle Plugin (AGP) ≥ 7.0[^1],若使用旧版本需降级处理: ```kotlin // AGP < 7.0 的兼容写法 applicationVariants.all { val variant = this val timestamp = ... variant.outputs.forEach { it.outputFileName = "app_${variant.name}_...apk" } } ``` 2. **特殊字符处理** 若版本号含空格,需替换为安全字符: ```kotlin val safeVersion = versionName.replace(" ", "_") ``` 3. **构建缓存** 带时间戳的文件名会**绕过构建缓存**,每次构建均生成新文件。若需保留缓存,可: ```kotlin // 仅 Release 版本添加时间戳 if (variant.buildType == "release") { output.outputFileName.set("..._${timestamp}.apk") } ``` ### 输出示例 | 变体名称 | 输出文件名 | |----------|------------| | devDebug | `app_devDebug_v1.0.0-dev_202405301430.apk` | | prodRelease | `app_prodRelease_v1.0.0_202405301430.apk` | --- ### 相关问题 1. 如何在文件名中添加 Git 提交 ID 替代时间戳? 2. 如何为不同 flavor 配置完全不同的文件名规则? 3. 文件名添加时间戳后如何自动上传到 Firebase? 4. 时间戳导致 APK 文件过多时如何自动清理旧文件? [^1]: 基于 Android Gradle Plugin 兼容性实践
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值