Andriod开发学习——Android中实现多级嵌套AAR包
AAR嵌套打包——flat-aar-android
Android studio 的 module 打包一般会打包成 AAR 的形式。如果 module 引用了一个本地的 aar,在打包的时候,这个被引用的 aar 是不会打包进去的,这种设计思想是比较好的,可以很好的避免包冲突的问题。例如 App 引用了两个 aar(1.aar 和 2.aar),1.aar 和 2.aar 都依赖了第三方的 3.aar,如果1.aar 和 2.aar 都把 3.aar 打包进去的话,App 再引用 1.aar 和 2.aar 的时候就会因为都有 3.aar 发生引用冲突。
但是在实际SDK开发过程中,会遇到一些很难处理的情况:例如module1 引用了一个本地 AAR1,且依赖另一个module2,module2引用了另一个本地 AAR2,这该如何将module1打包成aar(需要包含 AAR1,module2以及 AAR2)?——本篇文章就会介绍,这种情况的处理方式。
api和implementation之间的区别。
api:当前module可以使用,依赖当前module的module也可以使用。
implementation:只有当前module可以使用,其他module需要单独重新依赖才可以使用。
一. mModule1 直接依赖 aar(单一依赖)
-
拷贝 aar 至 mModule1/libs 目录
-
在mModule1/build.gradle 添加以下代码
/ 根节点下添加 repositories 节点
repositories {
flatDir {
dirs 'libs'
}
}
// dependencies节点内添加依赖
dependencies {
// aar-name 为 mModule 要依赖的 aar 包的名称,不包含后缀。
implementation(name: ‘aar-name’, ext: 'aar')
}
二. mModule1 依赖mModule2,mModule2引用了aar(嵌套依赖)
首先明确一点的是 Android 没有帮我们实现这一点,如果你针对单个模块打包 AAR 是不会把它依赖 module 一起打进去的。所以也延伸出下面几种尝试解决的方式:
最简单方案:把所有 module 合并成一个 module,变成单独 module 打包的形式,全部交给 Android Studio 帮我们完成,但这又走回了原先代码未拆分前的老路,最不该优先考虑的方案。
使用 maven 管理 aar 依赖方案:使用 maven 仓库进行远程依赖时,可在其 POM 文件中看到依赖关系,添加依赖时会自动导入其依赖的其他库。对于这种多个 module 的情况,我们可以把每个 module 都上传到 maven 库,因为上传时会 POM 文件中会保留 module 之间的依赖关系,最终用户添加某一库时也能正确引入其他依赖的库。缺点是使用这种方式通常需要 gradle 构建,在兼容一些旧版本的 Unity Editor 或其他场景下最好是能直接提供库文件,可作为保留方案考虑。
使用 fat-aar 方案: 开源项目一开始 android-fat-aar 就是为了解决这个问题而诞生的,可惜后续开发者也没有在维护了,但是里面一些实现思路是值得借鉴的,通过看 fat-aar.gradle 实现脚本你就知道,这是针对特地版本下的构建环境,把 module 构建 aar 时依赖的其他模块进行 copy 以及合并进来打包,这部分对开发者来说是透明的,所以整个流程足够简单又够用。但是因为项目输出结构跟构建环境有关,所以兼容性是个很大的问题,当然如果你也可以自己去兼容新版的 gradle 构建环境,本文附录可以参考实现。
本文实现的方案就是采用 fat-aar 方案,其实现过程如下:
使用 fat-aar 方案:
1. 在build.gradle(project)中添加 flat-aar 路径
dependencies {
classpath 'com.github.kezong:fat-aar:1.3.6'
}
2. 在需要依赖嵌套aar的Module中的build.gradle中add plugin
apply plugin: 'com.kezong.fat-aar'
3. 在Module中(有AAR包)中更改依赖
dependencies {
implementation fileTree(dir: 'libs', include: '*.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(name: 'lib-aar-local2', ext: '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')
}
多级本地依赖
如果你想将本地所有相关的依赖项全部包含在最终产物中,你需要在你主library中对所有依赖都加上embed
关键字
比如,mainLib依赖lib1,lib1依赖lib2,如果你想将所有依赖都打入最终产物,你必须在mainLib的build.gradle
中对lib1以及lib2都加上embed
关键字。
三、实例演示
如图所示,Module3依赖Module0、Module1、Module2,其中Module1、Module2都依赖Module0,每个Module都引用一个aar包,如何将分别Module0、1或者Module0、2打包到Module3生成新的aar?
具体实现步骤如下:
1. 在build.gradle(project)中添加 flat-aar 路径
dependencies {
classpath 'com.github.kezong:fat-aar:1.3.6'
}
2. 在Module3中的build.gradle中add plugin,add depencies
apply plugin: 'com.kezong.fat-aar'
android {
flavorDimensions 'environment'
productFlavors{
total{
dimension "environment"
buildConfigField("int","algorithmType","-1")
}
Module1{
dimension "environment"
buildConfigField("int","algorithmType","0")
}
Module2{
dimension "environment"
buildConfigField("int","algorithmType","1")
}
}
repositories {
flatDir { dirs 'libs'}
}
}
dependencies {
//传统依赖
api project(path: ':Module0')
api project(path: ':Module1')
api project(path: ':Module2')
//在打包时的任务指令(打包release包)
def taskName = gradle.startParameter.taskNames
for(def name:taskName){
def ts = name.toString().toLowerCase()
if(ts.contains('release') && !ts.contains('app')){
embed project(path: ':Module0', configuration: 'default')
Module1Embed project(path: ':Module1', configuration: 'default')
Module2Embed project(path: ':Module2', configuration: 'default')
totalEmbed project(path: ':Module1', configuration: 'default')
totalEmbed project(path: ':Module2', configuration: 'default')
break
}
}
}
3. 在Module1、2中的build.gradle中add plugin,add depencies
plugins {
id 'com.kezong.fat-aar'
}
dependencies {
//传统依赖
api project(path: ':evaCore')
api(name: 'aar1', ext: 'aar') //Module1
api(name: 'aar2', ext: 'aar') //Module2
//在打包时的任务指令(打包release包)
def taskName = gradle.startParameter.taskNames
for(def name:taskName){
def ts = name.toString().toLowerCase()
if(ts.contains('release') && !ts.contains('app')){
embed(name: 'aar1', ext: 'aar')//Module1
embed(name: 'aar2', ext: 'aar')//Module2
break
}
}
}
4. 在Module0中的build.gradle中add plugin,add depencies
plugins {
id 'com.kezong.fat-aar'
}
dependencies {
//传统依赖
api(name: 'aar0', ext: 'aar')
//在打包时的任务指令(打包release包)
def taskName = gradle.startParameter.taskNames
for(def name:taskName){
def ts = name.toString().toLowerCase()
if(ts.contains('release') && !ts.contains('app')){
embed(name: 'aar0', ext: 'aar')
break
}
}
}
附录
附录一:远程依赖
如果你想将所有远程依赖在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
transitive = false
}
附录二:Gradle版本支持
Version | Gradle Plugin | Gradle |
---|---|---|
1.0.1 | 3.1.0 - 3.2.1 | 4.4 - 6.0 |
1.1.6 | 3.1.0 - 3.4.1 | 4.4 - 6.0 |
1.1.10 | 3.0.0 - 3.4.1 | 4.1 - 6.0 |
1.2.6 | 3.0.0 - 3.5.0 | 4.1 - 6.0 |
1.2.8 | 3.0.0 - 3.5.9 | 4.1 - 6.8 |
1.2.11 - 1.2.14 | 3.0.0 - 3.6.9 | 4.1 - 6.8 |
1.2.15 - 1.2.16 | 3.0.0 - 4.0.2 | 4.1 - 6.8 |
1.2.17 | 3.0.0 - 4.0.2 | 4.9 - 6.8 |
1.2.18+ | 3.0.0 - 4.1.0 | 4.9 - 6.8 |
1.3.+ | 3.0.0 - 4.1.0 | 4.9 - 6.8 |
1.3.4 | 3.0.0 - 4.1.0 | 4.9+ |