联系我们
简单又实用的WordPress网站制作教学
当前位置:网站首页 > 程序开发学习 > 正文

【Gradle KTS】一次发布 Android SDK 到 maven 仓库的经验总结

作者:访客发布时间:2023-12-28分类:程序开发学习浏览:737


导读:前言近期接到需求,将此前一个项目的部分功能封装成SDK对外提供,我在SDK开发方面没有经验,借着这个需求,了解了SDK开发的一些技巧,也爬了很多的坑,梳理出这篇文章,...

前言

近期接到需求,将此前一个项目的部分功能封装成 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

image.png

2. 配置自己的项目参数

image.png

3. 顺带提一下 application 和 library 的 gradle 配置区别

  1. 插件从 com.android.application 变成了 com.android.library

  2. library 模块不需要,也不应该配置 applicationId , targetSdk , versionCode , versionName

    image.png

产出 aar 包

产出的方式有很多,这里提供常见的 2 种

1. gradlew 命令产出(我喜欢用的)

./gradlew :SdkDemo:assembleRelease

# 如果你配置了 variant,不确定具体的产出命令,也可以先使用 tasks 命令查看所有命令,从中找到你需要的
./gradlew :SdkDemo:tasks

这里命令中的 SdkDemo 对应着我的 module 名称,具体到你的项目中,换成你的即可,下文同理。

2. AS 自带的 gradle 插件产出

image.png

最终 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

image.png

  • after

image.png

所谓 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实践及原理分享


标签:仓库经验KTSGradle安卓系统maven


程序开发学习排行
最近发表
网站分类
标签列表