Fat-aar 打包方案应用过程的问题
fat-aar 是当前最流行的,将一个 module 及它所依赖的 module, aar /jar 库合并打包成为一个 aar 文件的方案。当前 fat-aar 最新版本是 1.4.4,可以支持 AGP 8.13,gradle 8.14 版本。
当前 fat-aar 方案处在维护阶段,后续是否会有持续更新不得而知,因为 google 官方推出了一个类似的 Fused Library 方案。但是,这个方案目前而言太新,从 AGP 8.12 才引入,对于较老的项目而言,一旦更新 AGP 版本,可能引起很多构建问题,不是很有好。
另外,从已知的 Fused Library 问题看,它还需要解决使用比较频繁的 local aar/jar 依赖问题。下面是已知问题(官网介绍)。
- 无法生成源 JAR 文件
- 添加对其他 .aar 文件的文件依赖项
- 不支持融合 RenderScript 和 Prefab 制品
fat-aar 打包实现只适用于 library 属性的 module,目标将 library module 和它依赖的多个 (remote/local)aar/jar 文件合并打包,成为一个最终的 big aar 文件。
fat-aar 对不同类型依赖库的处理方式:
-
aar 库 —— 库文件内字节码,资源等文件被减压缩后,再与 library module 的字节码文件,资源文件等合并打包到新的 aar 文件。
-
jar 库 —— jar 文件会被以文件方式置于目标 aar 文件的
libs/xxx.jar路径下。


应用打包问题
在引入 fat-aar 方案到项目中实际应用,遇到的情况会有不同,下面提及的问题,是设置了依赖传递的情况。
不同类型库的表现
fat-aar 对 module 依赖 local 和 remote 库表现不同。
- 对于 local aar/jar 库,需要 local aar/jar 库内包含的是实现的 class 字节码,资源等文件,这样 module embed 依赖时,可以直接减压缩库文件,并最终合并打包这些字节码,资源等文件。
- 对于 remote 依赖库:
- 一级依赖是 aar 类型库,基本是完整的 aar 结构,可以完整的依赖。
- 如果有二级依赖,fat-aar 解析 pom 类型的文件,进一步解析依赖库。
- 如果二级依赖是类似 okhttp 库,其内包含的是 kotlin 类型的元数据文件,fat-aar 没能正确解析元数据,最终打包的 aar 文件在运行时出错。
依赖传递引发的错误
对于二级依赖,若作了以下 fat-aar 设置,即使用库的依赖传递。
// lib_module/build.gradle.kts
fataar {
/**
* If transitive is true, local jar module and remote library's dependencies will be embed. (local aar module does not support)
* If transitive is false, just embed first level dependency
* Default value is false
* @since 1.3.0
*/
transitive = true
}
在这个配置下,fat-aar 在打包过程中,会解析一级依赖的库的依赖,如下 dependencies block 内的依赖。
// lib_module/build.gradle.kts
dependencies {
// ...
embed("com.google.firebase:firebase-storage:21.0.2")
}
fataar {
transitive = true
}
构建时,console log 打印:
[fat-aar][embed detected][aar]com.google.firebase:firebase-storage:21.0.2
[fat-aar] - [embed detected][transitive][aar]com.google.firebase:firebase-appcheck:17.1.0
fat-aar 正确解析依赖后会去下载 firebase-storage 依赖的 firebase-appcheck 库。
然而,配置 fat-aar 依赖传递也可能导致错误。
在正确解析并下载了依赖的 aar/jar 文件后,执行合并文件过程中,可能遇到不同的权限问题。
-
Execution failed for task ':lib_module:mergeClassesDebug'. > Cannot access output property '$1' of task ':lib_module:mergeClassesDebug'. Accessing unreadable inputs or outputs is not supported. Declare the task as untracked by using Task.doNotTrackState(). For more information, please refer to https://docs.gradle.org/8.12.1/userguide/incremental_build.html#sec:disable-state-tracking in the Gradle documentation. > java.nio.file.AccessDeniedException: lib_module/build/intermediates/fat-aar/merge_classes/debug/META-INF/java.com.google.android.libraries.abuse.recaptcha.enterprise.public_public.kotlin_module上面错误提示,gradle 构建过程中,遇到了错误的 input/output 问题。这个可能是因为执行过程,不同线程对于文件的锁定状态错误导致锁死,致使后来的线程不能访问文件所致。可以按照提示,设置
Task.doNotTrackState()不监视文件状态,修复问题。但又出现下面的问题。 -
* What went wrong: Execution failed for task ':lib_module:mergeClassesDebug'. > Could not copy zip entry 'lib_module/build/intermediates/exploded-aar/androidx.core/core-ktx/1.8.0/debug/classes.jar!META-INF/androidx.core_core-ktx.version' to 'lib_module/build/intermediates/fat-aar/merge_classes/debug/META-INF/androidx.core_core-ktx.version'. > lib_module/build/intermediates/fat-aar/merge_classes/debug/META-INF/androidx.core_core-ktx.version (Permission denied)上面错误提示,gradle
mergeClassesXxx任务,意图将不同库中的类文件,资源文件分类合并(merge)过程中,遇到了错误。尝试通过修改构建脚本,在
mergeClassesXxx任务执行前,手动创建目录META-INF目录,并修改权限为rwx权限,达到合并各类文件的目标,无法解决问题。因为META-INF目录是在任务执行过程中创建,合并文件动作也是在任务执行过程中进行,不能在mergeClassesXxx任务执行前,对目录权限进行前修改。
下面来看下针对上述问题的修改方案。
解决方案
先将 lib_module 打包成 lib_module aar 文件,查看它的内部结构如下。

分析上面的结构,查看结构内需要依赖的类似网络库 okhttp 的字节码文件等并未被打包进入到 aar 文件中。
若将这个 aar 文件置于 app module 的 libs 目录中,并在 app/build.gradle(.kts) 文件中设置这个 aar 库的依赖,程序在执行过程中,肯定会发生异常,甚至 app 的首个页面也无法展示。
问题原因
aar 文件内不包含功能上必须依赖的 okhttp 等库的字节码,它的原因在于 lib_module/build.gradle(.kts) 的依赖方式。
原来依赖配置如下
// lib_module/build.gradle.kts
// firebase 依赖
implementation(platform(libs.com.google.firebase.firebase.bom3))
implementation("com.google.firebase:firebase-database")
implementation("com.google.firebase:firebase-storage")
// okhttp 依赖
implementation("com.squareup.okhttp3:okhttp:5.1.0") //
implementation("com.squareup.okhttp3:logging-interceptor:5.1.0") //
在 module 中 implemention 依赖的库,在打包 aar 过程中,库内的字节码,资源等文件不会被打包进入最终的 aar 库文件。
另外,app 中设置了 module aar 依赖后可以成功运行,可以在 app/build.gradle(.kts) 配置中查看,应该可以看到有与 library module 相同的依赖。apk 打包过程中会将运行需要的所有的依赖库都合并打包在 apk 文件中。apk 打包与 library 打包在 implementation 依赖的处理机制上不同。
解决方案
上面的合并问题,以及打包中的权限问题,根据下面的方式作调整。
首先,修改依赖方式,将原来的 implementation 依赖修改为 embed 依赖。
其次,合并的权限问题,禁止使用依赖传递功能。
核心:关闭依赖传递,将需要的二级依赖库单独 embed 依赖。
禁止 fat-aar 依赖传递(默认值)。
// lib_module/build.gradle.kts
fataar {
transitive = false
}
构建过程中,fat-aar 依赖传递下会打印 transitive 日志。下面是部分库的依赖传递的日志。
[fat-aar][embed detected][jar]com.squareup.okhttp3:logging-interceptor:5.1.0
[fat-aar] - [embed detected][transitive][jar]com.squareup.okhttp3:okhttp-jvm:5.1.0
[fat-aar] - [embed detected][transitive][jar]com.squareup.okio:okio-jvm:3.15.0
[fat-aar] - [embed detected][transitive][jar]org.jetbrains:annotations:23.0.0
[fat-aar][embed detected][aar]com.google.accompanist:accompanist-permissions:0.37.3
[fat-aar] - [embed detected][transitive][aar]androidx.activity:activity-compose:1.9.0
[fat-aar] - [embed detected][transitive][aar]androidx.activity:activity-ktx:1.9.0
[fat-aar] - [embed detected][transitive][aar]androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2
[fat-aar][embed detected][jar]com.google.code.gson:gson:2.13.2
[fat-aar] - [embed detected][transitive][jar]com.google.errorprone:error_prone_annotations:2.41.0
[fat-aar][embed detected][aar]com.google.android.play:integrity:1.4.0
[fat-aar] - [embed detected][transitive][aar]com.google.android.gms:play-services-tasks:18.2.0
[fat-aar] - [embed detected][transitive][aar]com.google.android.gms:play-services-basement:18.5.0
[fat-aar] - [embed detected][transitive][aar]androidx.fragment:fragment:1.1.0
......
上面 transitive 行表示的就是二级依赖的库。修改上,根据二级依赖,将二级依赖库单独通过 embed 依赖,设置到 lib_module/build.gradle.kts 文件中。
embed 依赖库时, androidx 开始的库,及部分三方库不设置,主要因为:
- androidx 开头的库,在 app 开发时,工程创建伊始默认就设置依赖库中。若包含在 module aar 文件,容易造成 duplicate classes 构建错误。
- 若 androidx 库均被包含进 aar 文件,造成 aar 文件体积过大。
修改后的,lib_module/build.gradle.kts 依赖配置文件如下。
// lib_module/build.gradle.kts
dependencies {
// .......
implementation(libs.androidx.core.ktx)
implementation(libs.appcompat)
implementation(libs.material)
// remote 库内是 kotlin 元数据包,embed 依赖不能正确解析二级依赖,会造成最终 apk 错误。
// embed(libs.okhttp.logging.interceptor)
embed(mapOf("name" to "logging-interceptor-5.1.0", "ext" to "jar"))
embed(mapOf("name" to "okhttp-android", "ext" to "jar"))
embed(mapOf("name" to "okio-jvm-3.15.0", "ext" to "jar"))
// ......
}
配置修改为 embed 依赖前,可以先单独下载目标依赖文件,查看具体文件内包含的是字节码等文件还是二级依赖的配置文件,又或是元数据文件,根据具体的情况作出适合的调整。
同时,需要将 app/build.gradle(.kts) 中重复的依赖作移除操作。有库依赖冲突的情况,可以在 app/build.gradle(.kts) 视情况作 exclude 操作。
最后打包的结构图,google 库,okhttp 库被打包进入了 aar 文件。

应用 fat-aar 过程,尤其是设置了传递依赖,可能会有若干问题发生。
大家视情况调整。

3958

被折叠的 条评论
为什么被折叠?



