Fataar 打包方案应用过程的问题

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 对不同类型依赖库的处理方式:

  1. aar 库 —— 库文件内字节码,资源等文件被减压缩后,再与 library module 的字节码文件,资源文件等合并打包到新的 aar 文件。

  2. jar 库 —— jar 文件会被以文件方式置于目标 aar 文件的 libs/xxx.jar 路径下。


应用打包问题

在引入 fat-aar 方案到项目中实际应用,遇到的情况会有不同,下面提及的问题,是设置了依赖传递的情况。

不同类型库的表现

fat-aar 对 module 依赖 local 和 remote 库表现不同。

  1. 对于 local aar/jar 库,需要 local aar/jar 库内包含的是实现的 class 字节码,资源等文件,这样 module embed 依赖时,可以直接减压缩库文件,并最终合并打包这些字节码,资源等文件。
  2. 对于 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 文件后,执行合并文件过程中,可能遇到不同的权限问题。

  1. 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() 不监视文件状态,修复问题。但又出现下面的问题。

  2. * 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 开始的库,及部分三方库不设置,主要因为:

  1. androidx 开头的库,在 app 开发时,工程创建伊始默认就设置依赖库中。若包含在 module aar 文件,容易造成 duplicate classes 构建错误。
  2. 若 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 过程,尤其是设置了传递依赖,可能会有若干问题发生。

大家视情况调整。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zen@sz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值