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 的生命周期可以分为三个阶段:

  1. 初始化阶段:Gradle 支持单项目和多项目构建,在初始化阶段,Gradle 确定哪些项目将参与构建,并为每个项目创建 Project 实例,比如解析 settings.gradle 文件,以此来决定是单项目构建还是多项目构建。
  2. 配置阶段:解析每个工程的 build.gradle 文件,创建要执行的任务子集和确定各种任务之间的关系,并对任务做一些初始化配置。
  3. 运行阶段: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。

  1. 创建渠道信息文件

接下来,我们需要创建一个用于存放渠道信息的文件。这个文件通常是一个JSON格式的文件。你可以创建一个名为channels.json的文件,内容如下:

{

"channels": [

"google",

"huawei",

"xunlei",

"xiaomi"

]

}

代码注释:该JSON文件列出了所有的渠道名称,后续将会用于生成不同的APK。

  1. 编写打包逻辑

在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文件。

  1. 执行打包

完成上述步骤后,你只需在命令行中运行以下命令来启动打包:

./gradlew multiChannelBuild

代码注释:此命令启动了我们定义的multiChannelBuild任务,生成各个渠道的APK文件。

  1. 检查输出

打包完成后,APK文件将会在app/build/outputs/apk目录下生成。你可以检查每个渠道对应的APK是否生成成功。

三、旅行图与序列图

接下来,我们用图形方式帮助理解整个打包流程。

  1. 旅行图

  2. 序列图

在本文中,我们详细介绍了如何使用Kotlin DSL在Android中实现多渠道打包。你了解了每一步所需的配置及代码,它们如何共同工作以生成多个APK文件。

希望这篇文章能够帮助你更好地理解Android多渠道打包的原理和实现。

相关推荐
monkey_meng几秒前
【Rust Iterator 之 fold,map,filter,for_each】
开发语言·后端·rust
Vae_Mars4 分钟前
QT-protected
开发语言·qt
JosieBook13 分钟前
【面试题】2025年百度校招Java后端面试题
java·开发语言·网络·百度
wjs202429 分钟前
CentOS Docker 安装
开发语言
阿熊不会编程1 小时前
【计网】自定义协议与序列化(一) —— Socket封装于服务器端改写
linux·开发语言·网络·c++·设计模式
小牛itbull1 小时前
Mono Repository方案与ReactPress的PNPM实践
开发语言·前端·javascript·reactpress
jokerest1231 小时前
pwn——test_your_nc1——测试
开发语言·php
碧海蓝天20222 小时前
接上一主题,C++14中如何设计类似于std::any,使集合在C++中与Python一样支持任意数据?
开发语言·c++·python
周杰伦fans2 小时前
Java与C#
java·开发语言·c#
SAP学习成长之路2 小时前
SAP 零售方案 CAR 系统的介绍与研究
大数据·开发语言·sap·abap·零售·car·bapi