【Gradle KTS】一次发布 Android SDK 到 maven 仓库的经验总结
作者:访客发布时间:2023-12-28分类:程序开发学习浏览:387
前言
近期接到需求,将此前一个项目的部分功能封装成 SDK 对外提供,我在 SDK 开发方面没有经验,借着这个需求,了解了 SDK 开发的一些技巧,也爬了很多的坑,梳理出这篇文章,把一些经验分享给大家。
如有不足或者谬误,还请各位同行大哥们多多指教。
jar VS aar
jar 包
jar(Java ARchive) 是 java 归档文件,对于我们 Android 开发来说,通俗的理解就是这是一个单纯提供业务能力的 SDK,只有 class 文件与清单文件,不涉及任何的页面,不包含资源文件。
aar 包
aar(Android ARchive) 同理,是 Android 归档文件,也就是除了 class 文件和清单文件之外,还会包含一些 Android 页面,资源文件,SDK 本身就具备 UI 交互能力。
我这次接到的需求就是包含 UI 交互的封装,因此本文后续都将围绕 aar 展开内容。
构建并产出 aar 包
这一部分我不做详细阐述,因为类似的文章很容易检索到,我也会在文末贴一些相关的文章链接
创建 library module
我看有些文章,是把 application 项目改成 libaray ,我个人的情况还是提取封装部分功能,所以这里还是演示新建 libaray
1. File --> New --> New Module
2. 配置自己的项目参数
3. 顺带提一下 application 和 library 的 gradle 配置区别
-
插件从
com.android.application变成了com.android.library -
library模块不需要,也不应该配置applicationId,targetSdk,versionCode,versionName
产出 aar 包
产出的方式有很多,这里提供常见的 2 种
1. gradlew 命令产出(我喜欢用的)
./gradlew :SdkDemo:assembleRelease
# 如果你配置了 variant,不确定具体的产出命令,也可以先使用 tasks 命令查看所有命令,从中找到你需要的
./gradlew :SdkDemo:tasks
这里命令中的 SdkDemo 对应着我的 module 名称,具体到你的项目中,换成你的即可,下文同理。
2. AS 自带的 gradle 插件产出
最终 aar 包体都会产出在 SdkDemo/build/outputs/aar/ 路径下
发布 aar 包
通常情况下,此时直接产出 aar 包输出使用的话,大概率会报找不到类的异常,原因很简单:打包时没有包含使用的依赖,或者宿主项目中没有所需要的依赖。基于上述的原因也会有两种不同的解决方案:
将依赖打进 aar 包后再发布
我们平时使用的依赖,很多也是以 aar 包的形式从远端仓库,下载到我们本地,然后再参与遇到编译流程中去的。
那将依赖打进 aar 其实也就是将多个 aar 合并成一个,这种方式,Google 目前默认是不是支持的,需要使用第三方的 gradle 插件来实现
fat-aar-android
这个插件做的事情简单来说就是把多个 aar 包解包,然后合并诸如 Manifest,res 资源,R 文件,assets,libs,jni 等各类文件,其中合并 R文件 是最重要的一步。
1. 添加 fat-aar 插件
在 project 的 build.gradle.kts 添加 fat-aar 插件
buildscript {
dependencies {
···
classpath("com.github.kezong:fat-aar:1.3.8")
// 本插件作者已停更,如果你和我一样使用 agp 8.0 及之后版本,可使用其他贡献者适配的版本
// classpath("com.github.aasitnikov:fat-aar-android:ce932b38ef")
}
}
在 SDKDemo module 的 build.gradle.kts(本节后文同) 添加 fat-aar 插件
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
id("com.google.devtools.ksp")
id("maven-publish")
}
2. Embed dependencies
embed 你所需要合并打入的依赖, 用法类似 implementation
dependencies {
implementation(fileTree(mapOf("dir" to "libs", "include" to "*.jar")))
// java dependency
embed(project(path = ":lib-java", configuration = "default"))
// aar dependency
embed(project(path = ":lib-aar", configuration = "default"))
// aar dependency
embed(project(path = ":lib-aar2", configuration = "default"))
// local full aar dependency, just build in flavor1
flavor1Embed(project(path = ":lib-aar-local", configuration = "default"))
// local full aar dependency, just build in debug
debugEmbed(mapOf("name" to "lib-aar-local2", "ext" to "aar"))
// remote jar dependency
embed("com.google.guava:guava:20.0")
// remote aar dependency
embed("com.facebook.fresco:fresco:1.12.0")
// don't want to embed in
implementation("androidx.appcompat:appcompat:1.2.0")
}
3. 执行 assemble 命令
# assemble all
./gradlew :SDKDemo:assemble
最终合并产物会覆盖原有aar,同时路径会打印在log信息中.
4. 多级传递
本地依赖
如果你想将本地所有相关的依赖项全部包含在最终产物中,你需要在你主library中对所有依赖都加上embed关键字
比如,mainLib依赖lib1,lib1依赖lib2,如果你想将所有依赖都打入最终产物,你必须在mainLib的build.gradle中对lib1以及lib2都加上embed关键字
远程依赖
如果你想将所有远程依赖在pom中声明的依赖项同时打入在最终产物里的话,你需要在build.gradle中将transitive值改为true,例如:
fataar {
/**
* If transitive is true, local jar module and remote library's dependencies will be embed.
* If transitive is false, just embed first level dependency
* Local aar project does not support transitive, always embed first level
* Default value is false
* @since 1.3.0
*/
transitive = true
}
如果你将transitive的值改成了true,并且想忽略pom文件中的某一个依赖项,你可以添加exclude关键字,例如:
embed("com.facebook.fresco:fresco:1.11.0") {
// exclude any group or module
exclude(group = "com.facebook.soloader", module = "soloader")
// exclude all dependencies
isTransitive = false
}
接下来你需要做的可能是和你的乙方联调确认依赖是否有冲突问题,如果有并且对方不能修改,那我建议看看下面的 maven 发布方式
发布到 maven 平台托管依赖
相比于前一种将多个 aar 重组打成一个包,发布到 maven 平台具有:
- 包体体积更小;
- 版本管理更加灵活可靠可追溯;
- 发布管理方便
等优点,缺点嘛,我觉得也是有的,配置上要更麻烦一点,尤其是当你跟我一样,项目全 kotlin 化的,kts 的 gradle 可是费了我不少时间,那下面就听我给你细说。
1. 添加 maven-publish 插件
在 SDKDemo module 的 build.gradle.kts(本节后文同) 添加 maven-publish 插件
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
id("com.google.devtools.ksp")
id("maven-publish")
}
2. 先打印一下 module 的 variant 配置,并配置发布的 variant
在 build.gradle.kts 的 最外层 android 节点同级添加 afterEvaluate 并打印 variant 配置信息
afterEvaluate {
publishing {
publications {
// 注意,这里的 release 是为发布任务定义的
create<MavenPublication>("release") {
components.forEach {
System.out.println(it.name)
}
}
}
}
在确认了你的 variant 名称之后,继续配置 MavenPublication
afterEvaluate {
publishing {
publications {
create<MavenPublication>("release") {
// components.forEach {
// System.out.println(it.name)
// }
// 注意,这里的 Release 本发布任务对应的 variant,从上一步打印的 variant 中选择填写
from(components.getByName("Release"))
groupId = "com.randalldev.sdkdemo" // 唯一标识(通常为模块包名,也可以任意)
artifactId = "sdkdemo" // 项目名称(通常为类库模块名称,也可以任意)
version = "v1.0.0" // 版本号
}
}
}
3. 此时你就可以先尝试执行发布本地 maven
再次执行查看 gradle 任务命令,查找 maven 发布命令
./gradlew :SdkDemo:tasks
应该会看到 publishReleasePublicationToMavenLocal 的命令
执行一下,确认没有报错,然后继续后续的配置
4. 添加你的 maven 仓库及账户配置
afterEvaluate {
publishing {
···
repositories {
maven {
// 如果你的仓库地址是私服且没有配置 https 证书,那就需要添加这行
isAllowInsecureProtocol = true
// 这里的名字自定义即可,用来标识你的仓库,会在 gradle 命令体现
name = "maven"
url = uri("你的仓库地址")
credentials {
username = "你的账户"
password = "你的密码"
}
}
}
}
}
5. 执行发布命令
再次执行查看 gradle 任务命令,查找 maven 发布命令
./gradlew :SdkDemo:tasks
应该会看到 publishReleasePublicationToMavenRepository 的命令
这一步完成,在前置步骤均顺利的情况下,就可以完成 module 的 maven 发布了
6. 建议调整一下依赖的编译 scope
publishing.publications.configureEach {
pom.withXml {
asNode().dependencies.'*'.findAll() {
it.scope.text() == 'runtime' && project.configurations.implementation.allDependencies.find { dep ->
println("replace dependency name from ${dep.name} to ${it.artifactId.text()}")
dep.name == it.artifactId.text()
}
}.each {
println("replace dependency scope from ${it.scope*.value} to compile")
it.scope*.value = 'compile'
}
}
}
这部分代码,我目前还没能实现 kts 化,所以暂时需要单独创建一个 SDKDemo 目录下的 scope.gradle 并 apply 进 的 maven 配置相关代码
afterEvaluate {
publishing {
publications {
···
}
apply(from = "scope.gradle")
repositories {
···
}
}
}
看下执行发布命令后产生的影响
- before
- after
所谓 scope是 pom 管理文件中用于定义依赖关系的标签,它规定了依赖的作用域,即依赖在何时可用、以及在构建的不同阶段中是否被包含。而 pom 是用来描述项目构建的基本信息,可以简单理解为 pom 文件中管理着所有依赖及其作用域。
以下是 maven 中常见的依赖作用域:
| 作用域 | 编译 | 测试 | 运行 | 包含到 JAR/WAR | 备注 |
|---|---|---|---|---|---|
| compile | 是 | 是 | 是 | 是 | 默认作用域,适用于所有阶段。 |
| provided | 是 | 是 | 否 | 是 | 编译和测试时需要,运行时由容器提供。 |
| runtime | 否 | 是 | 是 | 是 | 编译时不需要,但在运行时需要。 |
| test | 否 | 是 | 否 | 否 | 仅在测试阶段使用,不包含到最终构建。 |
| system | 否 | 否 | 是 | 是 | 与 provided 类似,但需要显式提供路径。 |
| import | - | - | - | - | 仅在 <dependencyManagement> 中使用。 |
我们 Android 项目以来添加,常使用 implementation,其对应着 runtime scope,通过将 scope 修改为 compile 可以避免一些依赖冲突问题,比如 duplicated class *******
7. 添加依赖即可使用了
将仓库地址和可访问的账户提供给使用者,其在项目的 settings.gradle.kts 中配置 maven 仓库
dependencyResolutionManagement {
···
repositories {
···
maven {
// 非 https 地址需要添加
allowInsecureProtocol(true)
credentials {
username = "乙方的账户"
password = "乙方的密码"
}
url = uri("maven 仓库地址")
}
}
}
并在需要使用的 module 中添加依赖
implementation("com.randalldev.sdkdemo:sdkdemo:v1.0.0")
参考文章
Maven Publish Plugin
使用maven-publish插件发布Android工件(kts)
fat-aar实践及原理分享
相关推荐
- 轻松上手:
(三)笔记可再编辑 - 如何在iPhone,iPad和Android上使用WordPress应用程序
- 一款简单高效的Android异步框架
- [Android][NDK][Cmake]一文搞懂Android项目中的Cmake
- Android---View中的setMinWidth与setMinimumWidth的踩坑记录
- Android广播如何解决Sending non-protected broadcast问题
- 有关Android Binder面试,你未知的9个秘密
- 开启Android学习之旅-2-架构组件实现数据列表及添加(kotlin)
- Android低功耗蓝牙开发总结
- Android 通知文本颜色获取
- 程序开发学习排行
- 最近发表


