Kotlin DSL Gradle 指南
本文是关于 Kotlin DSL Gradle 的指南(上篇),介绍了 Gradle 作为 Android 开发构建工具的作用及优势,包括初始配置、生命周期、依赖管理、Task 相关内容。如 Task 的创建、自定义、各种方法和属性,以及文件操作等,还提到了解决依赖冲突的方式。
Gradle 是用于 Android 应用程序开发的构建工具,可以帮助开发者管理项目的编译,打包,签名等任务,同时支持模块化开发,定制构建流程和多种签名配置,简化了项目管理和提高了开发效率。现在 Android 官方已经默认推荐使用 Kolin DSL 来构建 Gradle 了,那就一起来瞧瞧吧!
初始配置
新建一个项目,看看初始配置。
我们从上到下来看一看,相关配置的说明都在注释里。
Module 下的 build.gradle.kts
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
android {
//应用程序的命名空间,主要用于访问应用程序资源。
namespace = "com.xzj.myapp"
//编译所依赖的 Android SDK 版本。
compileSdk = 33
defaultConfig { //APP 的唯一标识
applicationId = "com.xzj.myapp"
//支持的最低 API Level,如指定23,表示低于 Android 6.0 的机型不能使用这个 APP。
minSdk = 23
//基于哪个 Android 版本开发的,如指定33,代表适配到 Android 13。
targetSdk = 33
//版本号
versionCode = 1
//版本名称
versionName = "1.0"
//指定运行测试时要使用的 InstrumentationRunner,AndroidJUnitRunner 是 Android 测试框架中的一个组件,用于执行针对 Android 应用程序的测试。
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
//表示在项目中使用支持库来处理矢量图像
vectorDrawables { useSupportLibrary = true }
}
//构建类型,主要用于打包。
buildTypes {
release {
isMinifyEnabled = false
proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" )
}
}
//指定 Java 环境版本
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
//指定 JVM 环境
kotlinOptions {
jvmTarget = "1.8"
}
//是否启用 compose
buildFeatures { compose = true }
//指定了 Compose 所使用的 Kotlin 编译器扩展的版本
composeOptions { kotlinCompilerExtensionVersion = "1.4.3" }
//表示在打包过程中排除项目中位于 "/META-INF/AL2.0" 和 "/META-INF/LGPL2.1" 路径下的资源文件
packaging { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" } } }
//依赖
dependencies {
implementation("androidx.core:core-ktx:1.9.0") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1") implementation("androidx.activity:activity-compose:1.7.0") implementation(platform("androidx.compose:compose-bom:2023.03.00")) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.material3:material3") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00")) androidTestImplementation("androidx.compose.ui:ui-test-junit4") debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-test-manifest")
}
gradle-wrapper.properties
#Thu Mar 21 18:07:31 CST 2024 #Gradle 压缩包解压后的主目录 distributionBase=GRADLE_USER_HOME
#Gradle 压缩包解压后的具体路径 distributionPath=wrapper/dists
#Gradle 的下载地址 distributionUrl=https://services.gradle.org/distributions/gradle-8.0-bin.zip
#存放 Gradle zip 压缩包的主目录。 zipStoreBase=GRADLE_USER_HOME
#存放 Gradle zip 压缩包的具体路径 zipStorePath=wrapper/dists
Project 下的 build.gradle.kts
//使用 Kotlin DSL 配置 Gradle 插件
plugins {
id("com.android.application") version "8.1.1" apply false
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
}
gradle.properties
#设置 JVM 的最大堆内存为 2048MB,指定了文件编码为 UTF-8。
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
#指示项目使用 AndroidX 库
android.useAndroidX=true
#指定 Kotlin 代码风格的规范,official 表示采用官方推荐的代码风格。
kotlin.code.style=official
#用于控制R类是否会传递依赖,当为 true 时,表示类不会被传递到依赖模块中,每个模块会有自己独立的 R 类。
android.nonTransitiveRClass=true
local.properties
## This file is automatically generated by Android Studio.
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
# # This file should *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
# # Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note. sdk.dir=/opt/android/sdk
settings.gradle.kts
//插件管理,指定插件下载的仓库。
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
//依赖管理,指定依赖库下载的仓库,此仓库是有顺序的,顺序决定先从哪个仓库下载。
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
//项目名称 rootProject.name = "MyApp"
//指定参与构建的模块,一个根工程可以有很多的 module,只有在这设置了,gradle 才会去识别,才会在构建的时候被包含进去。
include(":app")
生命周期
Task 是 Gradle 构建的核心,生命周期的意义就是在各个阶段把 Task 组合起来,按照我们的意图去构建项目。 Gradle 的生命周期可以分为三个阶段:
- 初始化阶段:Gradle 支持单项目和多项目构建,在初始化阶段,Gradle 确定哪些项目将参与构建,并为每个项目创建 Project 实例,比如解析 settings.gradle 文件,以此来决定是单项目构建还是多项目构建。
- 配置阶段:解析每个工程的 build.gradle 文件,创建要执行的任务子集和确定各种任务之间的关系,并对任务做一些初始化配置。
- 运行阶段:Gradle 根据配置阶段创建和配置的要执行的任务子集,执行任务。
Gradle 也可以监听各个阶段的回调
gradle.addProjectEvaluationListener(object : ProjectEvaluationListener {
//项目配置被评估之前触发,在这个阶段,Gradle 将会加载项目的构建脚本并准备执行项目的配置。
override fun beforeEvaluate(project: Project) {
println("beforeEvaluate")
}
//项目配置被评估之后触发,在这个阶段,所有的项目配置已经被加载和评估完成。
override fun afterEvaluate(project: Project, state: ProjectState) { println("afterEvaluate")
}
})
当项目配置被评估完成后,Gradle 就可以准确地知道项目的结构和需要执行的任务,从而能够开始真正的构建过程。因此,在项目配置被评估之前和之后,你可以通过监听相应的事件,在项目配置生命周期的不同阶段执行自定义的逻辑。
gradle.addBuildListener(object : BuildListener {
override fun settingsEvaluated(settings: Settings) {
println("settingsEvaluated")
}
override fun projectsLoaded(gradle: Gradle) {
println("projectsLoaded")
}
override fun projectsEvaluated(gradle: Gradle) {
println("projectsEvaluated")
}
override fun buildFinished(result: BuildResult) {
println("buildFinished")
}
})
Gradle 是构建工具本身,而 gradlew 是 Gradle Wrapper 提供的便捷工具,用于在项目中管理和执行 Gradle 构建任务。在 Android 项目中,通常使用 Gradle Wrapper 来统一团队的构建环境,简化项目的配置和维护。
依赖管理
Gradle 提供了强大的依赖管理支持,我们只需声明依赖项即可,Gradle 会帮我们找到所需的 Library,例如依赖项配置和依赖仓库。
dependencies {
implementation("androidx.core:core-ktx:1.9.0") implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1") implementation("androidx.activity:activity-compose:1.7.0") implementation(platform("androidx.compose:compose-bom:2023.03.00")) implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-graphics") implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.material3:material3") testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00")) androidTestImplementation("androidx.compose.ui:ui-test-junit4") debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-test-manifest")
}
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
需要注意的是,implementation 表示当前引用的第三方库仅限于本 Module 使用,api 表示的依赖是可以传递的。
Gradle 依赖管理进行了优化,如果项目存在同一个依赖库的多个版本,默认选择最高版本,Gradle 会自动排除重复的依赖,这在一定程度上防止了依赖冲突,但是为什么有时还是会遇到依赖冲突的问题呢?举个例子,某些第三方库可能会包含特定版本的某个库,并且你自己的项目中也引入了该库的不同版本,这种情况下,Gradle 会自动选择最高的版本,就可能发生依赖冲突。可以通过如下两种方式解决依赖冲突:
使用 exclude 排除依赖
implementation("com.google.android.exoplayer:exoplayer:2.14.0") {
exclude(group = "com.google.guava", module = "guava")
}
使用强制版本
configurations.all {
resolutionStrategy {
force("com.google.android.exoplayer:exoplayer:2.14.0")
}
}
Task
Task 是 Gradle 构建的核心对象,我们可以直接在 build.gradle 文件中创建。
创建 Task
tasks.register("xzj") { println("create task") }
也可以这样创建
task("xzj") { println("create task") }
这里需要注意的是:如果有 Task 同名的话,会编译失败。
自定义 Task
继承 DefaultTask,Action 的方法需要添加 @TaskAction 注解。
open class MyTask : DefaultTask() {
@get:Internal var taskName = "taskName"
@TaskAction fun action1() { println("action1: $taskName") }
@TaskAction fun action2() { println("action2: $taskName") }
@TaskAction fun action3() { println("action3: $taskName") }
}
注册自定义的 Task
tasks.register<MyTask>("myTask") { taskName = "xzj" }
然后我们就可以在 AndroidStudio 的 Gradle 工具面板,Tasks -> other 里找到这个 Task,双击执行即可。
如果是带构造函数传参的话,可以这样
open class MyTask @Inject constructor(private var taskName: String) : DefaultTask() { @TaskAction fun action1() {
println("action1: $taskName")
}
@TaskAction fun action2() {
println("action2: $taskName")
}
@TaskAction fun action3() {
println("action3: $taskName")
}
}
tasks.register<MyTask>("myTask","argument")
使用 @Inject 注解可以帮助 Gradle 正确地理解带参数的构造函数,并且在创建任务实例时能够正确地调用带参数的构造函数。
doFirst 和 doLast
使用了 doFirst 和 doLast 方法来分别添加在任务执行前和执行后需要执行的动作。
task("hello") {
doFirst { println("doFirst1") }
doFirst { println("doFirst2") }
println("hello")
doLast { println("doLast1") }
doLast { println("doLast2") }
}
执行这个 Task,输出如下:
doFirst 是倒序执行,doLast 是正序执行,Action 也是正序执行。
当你定义一个任务时,任务的配置代码会在 Gradle 构建脚本解析执行阶段就会执行,而不是在任务实际执行时才执行。因此,当 Gradle 解析执行到 println("hello") 这行代码时,会立即执行该代码并先打印出 hello。
dependsOn
dependsOn 用来定义任务之间的依赖关系,通过使用 dependsOn,可以确保一个任务在另一个任务之前执行,从而控制任务的执行顺序。举个例子,有两个任务 A 和 B,想让任务 B 在任务 A 执行完成后再执行,这样干:
tasks.register("taskA") {
doLast { println("Running task A") }
}
tasks.register("taskB") {
dependsOn("taskA")
doLast { println("Running task B") }
}
执行 taskB 会先执行 taskA
finalizedBy
dependsOn 指定的是上一个任务,而 finalizedBy 指定下一个任务,比如想让任务 A 执行后,再去执行任务 B,这样干:
tasks.register("taskA") {
finalizedBy("taskB")
doLast { println("Running task A")
}
}
tasks.register("taskB") {
doLast { println("Running task B")
}
}
onlyIf
onlyIf 是一个条件断言方法,用于指定任务是否应该执行的条件。当条件为 true 时,任务才会执行,否则任务将被跳过。
tasks.register("taskA") {
val flag = providers.gradleProperty("flag")
onlyIf {
//如果属性存在,则返回 true
flag.isPresent
}
doLast { println("Running A") } }
enabled
enable 是 Task 的开关,禁用之后任何操作都不会执行,可以实现和 onlyIf 一样的效果。
tasks.register("taskA") { enabled = true doLast { println("Running A") } }
查找 Task
tasks.findByName("taskA")?.doFirst { println("taskA findByName") }
tasks.findByPath("taskA")?.doFirst { println("taskA findByPath") }
tasks.named("taskA") { doLast { println("taskA named") } }
文件操作
open class WriteFileTask : DefaultTask() {
//任务输入参数 @Input var text = ""
//任务输出文件
@OutputFile var outputFile: File? = null
//任务运行时调用的方法
@TaskAction fun writeText() {
outputFile?.createNewFile()
outputFile?.writeText(text)
}
}
tasks.register("writeFileTask", WriteFileTask::class) {
text = "content"
outputFile = File(projectDir, "myFile.txt")
}
这里创建了一个写文件的 Task,执行之后就可以在相应的目录中看到创建的文件。
下面主要介绍了 Kotlin DSL Gradle 的相关内容,包括打包(apk 打包的流程、签名配置、多渠道打包、文件名修改)、打包成 jar 和 aar 及其应用、自定义插件(实现 Plugin 接口和创建 Task)、扩展插件(定义和使用扩展属性)。
打包
Gradle 作为构建工具,编译打包 apk 是 Gradle 主要作用之一,apk 由各种文件组成,比如代码文件和资源文件,可以理解为 Gradle 本质上是在帮我们管理这些散落在各处的文件。
签名配置
打包需要先配置签名信息,但是这些 AndroidStudio 都可以自动生成,几乎不用我们手动编写,下面来操作一下。
确认之后,在 app 模块的 gradle 中就会生成相关的代码。
android {
signingConfigs {
create("release") {
storeFile = file("/home/lbrd/Downloads/xzjKeystore.jks")
storePassword = "123456"
keyAlias = "key0"
keyPassword = "123456"
}
}
...
buildTypes {
release {
isMinifyEnabled = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" )
signingConfig = signingConfigs.getByName("release")
}
}
}
实际项目开发中一般不会直接把这些敏感信息写在这里,我们可以写在 gradle.properties 文件中,这里定义的属性都是全局的。
gradle.properties 中加入
storeFile=/home/lbrd/Downloads/xzjKeystore.jks storePassword=123456 keyAlias=key0 keyPassword=123456
引用其中定义的变量
signingConfigs {
create("release") {
storeFile = file(project.findProperty("storeFile") as String)
storePassword = project.findProperty("storePassword") as String
keyAlias = project.findProperty("keyAlias") as String
keyPassword = project.findProperty("keyPassword") as String
}
}
可以借助 AndroidStudio 的 Gradle 工具执行打包,如下所示:
有些人的 AndroidStudio 可能会没有 Gradle Task 工具,这时需要在设置中打开,然后同步一下即可。
build 完之后,我们就可以在如下路径找到对应的 apk 了。
这个 apk 的文件名太简单了,没有辨识度,我们来改一下,在 android {...} 中添加如下配置:
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
android.applicationVariants.all {
outputs.all {
if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) {
val config = project.android.defaultConfig
val versionName = config.versionName
val formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmm")
val createTime = LocalDateTime.now().format(formatter)
this.outputFileName = "${project.name}_${this.name}_${versionName}_$createTime.apk"
}
}
}
再次打包就会看到自定义的文件名了
多渠道打包
当我们的应用需要上架不同的应用市场,不同的渠道需要不同的定制化时,就需要多渠道打包。
先定义个 flavorDimensions
Add Product Flavor,添加具体的渠道。
不同的渠道可以配置不同的内容
这里配置了两个渠道,下面来看看 AndroidStudio 自动生成的 Gradle 代码。
android {
...
flavorDimensions += listOf("channel")
productFlavors {
create("huawei") {
dimension = "channel"
applicationId = "com.huawei.app"
}
create("xiaomi") {
dimension = "channel"
applicationId = "com.xiaomi.app"
}
}
}
配置之后,当我们执行打包的时候,就会有渠道的选择了。
这里全选,就会得到两个不同渠道的安装包文件夹。
在开发调试阶段,如果想直接运行成某个特定的渠道,而不是每次都打包所有的渠道,可以选择 Build Variant,这样调试的时候就会生成特定的渠道包了。
想要做不同渠道的定制化开发,就得在代码中获取到这些渠道,从而区别对待,可以通过 buildConfigField 修改 BulidConfig,BuildConfig 是在构建时自动生成的 Java 类,里面存放一些静态常量,编译后可以直接使用类中的常量。
android.buildFeatures.buildConfig = true
productFlavors {
create("huawei") {
dimension = "channel"
applicationId = "com.huawei.app"
buildConfigField("String", "CHANNEL_VALUE", ""huawei"")
}
create("xiaomi") {
dimension = "channel"
applicationId = "com.xiaomi.app"
buildConfigField("String", "CHANNEL_VALUE", ""xiaomi"")
}
}
然后直接通过 BuildConfig 获取即可
private fun getChannel() = BuildConfig.CHANNEL_VALUE
打包成 jar 和 aar
jar 包是 Java 平台的标准打包格式,只包含编译后的 .class 文件和资源文件,不包含 Android 资源文件,如布局,图片等。aar 包是 Android 平台的打包格式,可以包含编译后的 .class 文件和 Android 资源文件,如布局,图片等。 先创建一个 Android Library
直接打包这个 Module
aar 在这
jar 在这
有了 aar 和 jar 之后,就可以将其放入需要用到的 Module 中,可以放到 libs 文件夹中,然后引入即可,也可以发布到远程仓库,实现远程依赖。
implementation(files("libs/mylibrary-release.aar"))
我们可以修改生成的文件名和路径,创建一个 Task。
tasks.register<Copy>("createJar") {
// 先删除原来的
delete("libs/tool.jar")
// 拷贝的源文件路径
from("build/intermediates/aar_main_jar/release/")
// 目标路径
into("libs/") include("classes.jar")
// 重命名
rename("classes.jar", "tool.jar")
}
tasks.getByName("createJar").dependsOn("build")
执行这个 Task 即可
有些时候,需要把应用模块打包成 aar,我们得先把应用模块变成库模块,如下所示:
去掉之后 AndroidManifest 长这样
xml
代码解读
复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
<application>
<activity android:name=".MainActivity"
android:exported="true"
android:label="@string/app_name"
android:theme="@style/Theme.ComposeApp" />
</application>
</manifest>
同步一下,就由应用模块转变为库模块了,然后按照上面的方式打包即可。
自定义插件
Gradle 插件是一种用于扩展和定制 Gradle 构建系统行为的工具,负责处理 Android 应用程序的构建,打包,签名等任务,自定义插件只需实现 Plugin 接口,实现 apply 方法即可。
open class MyPlugin : Plugin<Project> {
override fun apply(target: Project) {
println("apply")
}
}
使用 apply 函数将插件应用到项目或模块中
apply<MyPlugin>()
我们可以发现,在实现的 apply 方法中,有个 Project 对象,而 task 是 Project 中的一个方法,所以也可以通过这个 Project 对象去创建 Task。
open class MyPlugin : Plugin<Project> {
override fun apply(target: Project) {
target.task("pluginTask") {
doLast { println("running pluginTask")
}
}
}
}
Plugin 接口的 apply 方法在编译阶段就会执行,所以在 sync 执行构建后就会有输出。上面的都只是输出,乍一看似乎并没有什么实际的作用,那这里就再举个实例,一个压缩文件的自定义插件。
apply<ZipPlugin>() open class ZipPlugin : Plugin<Project> {
override fun apply(target: Project) {
target.afterEvaluate {
val textFile = File(project.rootDir, "myFile.txt")
project.task("zip", Zip::class) {
archiveFileName.set("xzj.zip")
//设置压缩文件名
destinationDirectory.set(File("${target.buildDir}/custom"))
//设置输出目录 from(textFile)
//将文件添加到压缩包中
}
}
}
}
执行 Task,就可以在对应的目录上看到该压缩文件。
上面所说的插件都属于二进制插件,Gradle 还有种脚本插件,下面我们来创建一个脚本插件,也可以新建一个 gradle 文件,防止 build.gradle.kts 中代码臃肿。
afterEvaluate {
task("zip", Zip::class) {
val textFile = File(project.rootDir, "myFile.txt")
archiveFileName.set("xzj.zip") destinationDirectory.set(File("${project.buildDir}/custom"))
from(textFile)
}
}
引入脚本插件跟二进制插件的方式不太一样,该参数是外部 Gradle 脚本的路径,Project 下的 build.gradle.kts 中引入该插件,如下所示:
apply("plugin.gradle.kts")
如果路径不正确的话,会因为找不到而编译出错。比方说我们需要在 app 下的 build.gradle.kts 引入该插件,就应该这样:
apply("../plugin.gradle.kts")
扩展插件
在 android {...} 闭包里有各类配置,如 minSdk,targetSdk,versionCode 等等,我们也可以通过扩展插件来实现自定义配置。
定义一些扩展属性
open class Car {
var name: String = "BYD"
var type: String = "ocean"
}
在 plugin 中使用扩展属性
class MyPlugin : Plugin<Project> {
override fun apply(target: Project) {
val extension = target.extensions.create("test", Car::class.java) target.task("MyTask") {
doLast {
println("name: ${extension.name}")
println("type: ${extension.type}")
}
}
}
}
然后将插件引入
apply<MyPlugin>()
其实这还不够,我们先来看看 android{...} 这个闭包是怎么实现的?
fun org.gradle.api.Project.`android`(configure: Action<com.android.build.gradle.internal.dsl.BaseAppModuleExtension>): Unit = (this as org.gradle.api.plugins.ExtensionAware).extensions.configure("android", configure)
这里我们就仿照这个,定义一个扩展函数,用于配置自定义属性。
fun Project.customConfig(action: Car.() -> Unit) {
extensions.configure("test", action)
}
这里需要注意的是,extensions.configure 的 name,需要对应上面 extensions.create 的 name,不然会报错: Extension does not exist
这样就可以像配置 android{...} 一样,去配置我们的自定义属性了。
customConfig { name = "xiaomi" type = "SU7" }
。
Android KTS多渠道打包详解
在Android应用的开发过程中,尤其是在发布时,很常用到多渠道打包的技术。它能够帮助我们一次性构建多个版本的APK,这对于发布到各个应用市场非常有帮助。今天,我将带你一起探索Android KTS(Kotlin DSL)多渠道打包的整个流程。
一、流程概述
多渠道打包的流程大致可以分为以下几个步骤:
步骤 描述
1. 配置build.gradle 配置多渠道相关属性
2. 创建渠道信息文件 列出所有渠道的信息
3. 编写打包逻辑 使用Kotlin DSL编写多渠道打包逻辑
4. 执行打包 运行打包命令
5. 检查输出 输出APK路径,检查各渠道 APK
二、详细步骤
1. 配置build.gradle
首先,我们需要在项目的build.gradle文件中进行多渠道打包的基本配置。以下是一个示例:
android {
// 定义compileSdkVersion
compileSdkVersion(31)
defaultConfig {
applicationId = "com.example.myapp"
versionCode = 1
versionName = "1.0"
// 配置渠道分包设置
flavorDimensions("default")
productFlavors {
create("google") {
// Google渠道的种种配置
applicationIdSuffix = ".google"
versionNameSuffix = "-google"
}
create("huawei") {
// 华为渠道的种种配置
applicationIdSuffix = ".huawei"
versionNameSuffix = "-huawei"
}
}
}
}
代码注释:此代码展示了在build.gradle中如何定义渠道。我们使用productFlavors创建多个渠道,分别指定了不同的applicationIdSuffix和versionNameSuffix。
2. 创建渠道信息文件
接下来,我们需要创建一个用于存放渠道信息的文件。这个文件通常是一个JSON格式的文件。你可以创建一个名为channels.json的文件,内容如下:
{
"channels": [
"google",
"huawei",
"xunlei",
"xiaomi"
]
}
代码注释:该JSON文件列出了所有的渠道名称,后续将会用于生成不同的APK。
3. 编写打包逻辑
在Kotlin DSL中编写打包逻辑,可以通过 build.gradle.kts 文件完成。以下是详细示例:
import org.gradle.api.Project
import org.gradle.api.tasks.TaskAction
import org.gradle.api.DefaultTask
import org.gradle.kotlin.dsl.extra
// 自定义任务:多渠道打包
tasks.register<DefaultTask>("multiChannelBuild") {
group = "build"
description = "Builds multiple APKs for each flavor in channels.json"
// 设置任务的执行逻辑
doLast {
// 读取渠道信息文件
val channelsFile = file("path/to/channels.json")
val channelsJsonString = channelsFile.readText()
val channels = parseChannels(channelsJsonString) // 假定parseChannels是解析JSON的函数
// 遍历每个渠道
channels.forEach { channel ->
exec {
val variant = "assemble${channel.capitalize()}"
commandLine("gradlew", variant)
}
}
}
}
// 解析JSON的方法
fun parseChannels(json: String): List<String> {
// 实现你的JSON解析逻辑
}
代码注释:上面代码定义了一个Gradle任务multiChannelBuild,它会读取渠道信息,并依次为每个渠道执行打包命令。你需要实现parseChannels方法来解析JSON文件。
4. 执行打包
完成上述步骤后,你只需在命令行中运行以下命令来启动打包:
./gradlew multiChannelBuild
代码注释:此命令启动了我们定义的multiChannelBuild任务,生成各个渠道的APK文件。
5. 检查输出
打包完成后,APK文件将会在app/build/outputs/apk目录下生成。你可以检查每个渠道对应的APK是否生成成功。
三、旅行图与序列图
接下来,我们用图形方式帮助理解整个打包流程。
1. 旅行图
2. 序列图
在本文中,我们详细介绍了如何使用Kotlin DSL在Android中实现多渠道打包。你了解了每一步所需的配置及代码,它们如何共同工作以生成多个APK文件。
希望这篇文章能够帮助你更好地理解Android多渠道打包的原理和实现。