当前位置: 首页 > article >正文

Kotlin DSL Gradle 指南

本文是关于 Kotlin DSL Gradle 的指南(上篇),介绍了 Gradle 作为 Android 开发构建工具的作用及优势,包括初始配置、生命周期、依赖管理、Task 相关内容。如 Task 的创建、自定义、各种方法和属性,以及文件操作等,还提到了解决依赖冲突的方式。

Gradle 是用于 Android 应用程序开发的构建工具,可以帮助开发者管理项目的编译,打包,签名等任务,同时支持模块化开发,定制构建流程和多种签名配置,简化了项目管理和提高了开发效率。现在 Android 官方已经默认推荐使用 Kolin DSL 来构建 Gradle 了,那就一起来瞧瞧吧!

image.png

初始配置

新建一个项目,看看初始配置。

image.png

我们从上到下来看一看,相关配置的说明都在注释里。

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,双击执行即可。

image.png

如果是带构造函数传参的话,可以这样

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,输出如下:

image.png

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

image.png

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,执行之后就可以在相应的目录中看到创建的文件。

image.png

下面主要介绍了 Kotlin DSL Gradle 的相关内容,包括打包(apk 打包的流程、签名配置、多渠道打包、文件名修改)、打包成 jar 和 aar 及其应用、自定义插件(实现 Plugin 接口和创建 Task)、扩展插件(定义和使用扩展属性)。

打包

Gradle 作为构建工具,编译打包 apk 是 Gradle 主要作用之一,apk 由各种文件组成,比如代码文件和资源文件,可以理解为 Gradle 本质上是在帮我们管理这些散落在各处的文件。

签名配置

打包需要先配置签名信息,但是这些 AndroidStudio 都可以自动生成,几乎不用我们手动编写,下面来操作一下。

image.png

image.png

image.png

确认之后,在 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 工具执行打包,如下所示:

image.png

有些人的 AndroidStudio 可能会没有 Gradle Task 工具,这时需要在设置中打开,然后同步一下即可。

image.png

build 完之后,我们就可以在如下路径找到对应的 apk 了。

image.png

这个 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"

}

}

}

再次打包就会看到自定义的文件名了

image.png

多渠道打包

当我们的应用需要上架不同的应用市场,不同的渠道需要不同的定制化时,就需要多渠道打包。

先定义个 flavorDimensions

image.png

Add Product Flavor,添加具体的渠道。

image.png

image.png

不同的渠道可以配置不同的内容

image.png

这里配置了两个渠道,下面来看看 AndroidStudio 自动生成的 Gradle 代码。

android {

...

flavorDimensions += listOf("channel")

productFlavors {

create("huawei") {

dimension = "channel"

applicationId = "com.huawei.app"

}

create("xiaomi") {

dimension = "channel"

applicationId = "com.xiaomi.app"

}

}

}

配置之后,当我们执行打包的时候,就会有渠道的选择了。

image.png

这里全选,就会得到两个不同渠道的安装包文件夹。

image.png

在开发调试阶段,如果想直接运行成某个特定的渠道,而不是每次都打包所有的渠道,可以选择 Build Variant,这样调试的时候就会生成特定的渠道包了。

image.png

image.png

想要做不同渠道的定制化开发,就得在代码中获取到这些渠道,从而区别对待,可以通过 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

image.png

直接打包这个 Module

image.png

aar 在这

image.png

jar 在这

image.png

有了 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 即可

image.png

有些时候,需要把应用模块打包成 aar,我们得先把应用模块变成库模块,如下所示:

image.png

image.png

去掉之后 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,就可以在对应的目录上看到该压缩文件。

image.png

上面所说的插件都属于二进制插件,Gradle 还有种脚本插件,下面我们来创建一个脚本插件,也可以新建一个 gradle 文件,防止 build.gradle.kts 中代码臃肿。

image.png

 

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多渠道打包的原理和实现。
 


http://www.kler.cn/a/411099.html

相关文章:

  • Scala习题
  • 【算法】连通块问题(C/C++)
  • 【04】Selenium+Python 手动添加Cookie免登录(实例)
  • C++中的链式操作原理与应用(一)
  • uniapp vue2项目迁移vue3项目
  • 使用LLaMA-Factory微调时的数据集选择
  • MYSQL 表的增删改查(上)
  • qt ubuntu i386 系统
  • 【MySQL系列】通过创建新表备份`password`字段
  • c++:面向对象三大特性--继承
  • 数据结构 【二叉树(上)】
  • c++学习:json库例子
  • Spark SQL大数据分析快速上手-Hive安装
  • 【设计模式】【行为型模式(Behavioral Patterns)】之命令模式(Command Pattern)
  • Vue进阶面试题(三)
  • Python和R统计检验比较各组之间的免疫浸润
  • 【IEEE出版 | ISBN: 979-8-3315-0796-1 | 高录用稳检索】 2025神经网络与智能优化国际研讨会(NNIO 2025)
  • 中国科学院大学研究生学术英语读写教程 Unit6 Biology TextA 原文和翻译
  • 对于公平与效率的关系问题,材料中有两种不同倾向性的观点,请对这两种观点分别加以概述并谈谈你的看法。字数不超过500字。
  • 上海乐鑫科技一级代理商飞睿科技,ESP32-C61高性价比WiFi6芯片高性能、大容量
  • 鸿蒙应用的基本架构
  • OpenTK 实现三维空间模型仿真详解
  • 基于Springboot的心灵治愈交流平台系统的设计与实现
  • RSA非对称加密解,支持分段加密解密、(公钥加密,私钥解密)、(私钥加密,公钥解密)
  • Bean的生命周期详解保姆级教程,结合spring boot和spring.xml两种方式讲解,5/7/10大小阶段详细分析
  • 基于Matlab卷积神经网络的肺癌检测系统(良性、恶性及正常病例分类的综合分析)