Android 多 Module 合并打包 AAR

通常来说,我们在项目中引入第三方 SDK 通常有下面几种方式:

  1. 添加 JAR 包文件到项目依赖(对应 Java Library);
  2. 添加 AAR 包文件到项目依赖(对应 Android Library,包含资源文件、manifest等);
  3. 通过 Maven 仓库形式添加远程依赖 (远程依赖库文件,并且可以传递依赖关系);

Android Studio 创建 module 时可以选择类型,当我们选择 Android Library 的形式构建模块,可以打包得到一个 AAR 包。通常开发一个复杂业务流程的 SDK 项目跟一般应用项目一样,会按照业务拆分成多个 module 进行构建,与开发应用的区别在于,SDK 项目在开发阶段处于可随时编译运行状态,打包时要合并多个 module 产出 AAR 形式的 SDK 包。因为每个 module 都会生成对应的 aar 文件,但是一般来说我们不可能针对每个 module 进行单独打包让开发者接入,那么这时候问题就来了,可不可以把多 Module 合并打包成一个 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 构建环境。

有兴趣的还可以看看这篇文章,里面提到了一些手动修改文件的失败方案: Android 多module合并打包笔记 ,通常这种非常规的方案在万不得已的情况下最好别考虑,因为就算能用但操作也会相当麻烦而且容易出错,还是那句话能自动完成的工作就不要手动操作。

使用 fat-aar 方案:

第一步:引入 fat-aar.gradle 文件

fat-aar.gradle 文件复制到你的打包模块根目录,再修改 module 的 build.gradle 文件:

apply from: 'fat-aar.gradle'

或者直接从 URL 中获取

apply from: 'https://raw.githubusercontent.com/adwiv/android-fat-aar/master/fat-aar.gradle'

第二步:确定模块嵌套的依赖关系

这里可以使用 compile 和 embedded 两个字段来确定嵌套依赖关系,使用 embedded 添加依赖,表示这是要打包嵌套依赖的库。比如你的打包模块 moduleA 需要把其依赖 moduleB 和 moduleC 一起打包进去,而 support 包不需要一起打包进去,那么 moduleA 的 build.gradle 文件就该是 :

dependencies {
  compile fileTree(dir: 'libs', include: ['*.jar'])

  embedded project(':moduleB')
  embedded project(':moduleC')
  
  compile 'com.android.support:appcompat-v7:22.2.0'
}

使用 fat-aar 注意点:

兼容性:目前项目最新支持的 gradle 版本是 2.3.3 版本,新版本的 gradle 编译输出结果目录因为有变动,需要有针对性地重新适配才行。所以一定要注意项目使用的 gradle 版本:

buildscript {
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        // warning: current fat-aar only support gradle 2.3.3!
        //noinspection GradleDependency
        classpath 'com.android.tools.build:gradle:2.3.3'
    }
}

R 文件合并问题:如果打包模块和依赖的模块中都存在资源文件,也就是存在 R 文件的引用,这是需要注意 R 文件合并的问题。因为一个 ARR 包只会存在一个 R 文件,使用 fat-aar 打包时会把依赖模块中的 R 文件进行合并,但是因为原先模块中导入的 R 文件包名还是之前的模块的,所以会存在找不到 R 文件的错误。这里看到有其他人说通过修改脚本文件替换 R 文件 import 导包或者把全部资源文件放到打包模块,不过我觉得这样做都不太合理,于是我自己尝试了下面这种方案:

我们知道 R 文件是根据 AndroidManifest.xml 文件的 package 字段内容来生成的对应包名的,比如 module 中 AndroidManifest.xml 的 package 指定为 com.xx.xxx 时,那么该 module 内的 R 文件导包就是:import com.xx.xxx.R 了。我们可以利用这一点,把所有需要打包的 module 的 package name 改成统一的名称,这样的话,所有 R 文件导包就变成一致的了,就算合并后的 AAR 中只存在一个 R 文件也不会存在找不到的问题。

为了避免相同模块使用相同包名导致的编译失败的问题,我们可以使用 enforceUniquePackageName = false 配置各个 module 的 build.gradle 文件,这样可以不强制各 module 使用唯一包名。

BuildConfig 合并问题:因为每个模块也会对应生成 BuildConfig.class 文件,为了在开发阶段可以正常运行,我们还需要处理相同包名时各模块的 BuildConfig.class 合并的问题,这里我们可以选择设置 packageBuildConfig(false) 这样可以让依赖的子模块不把 BuildConfig.class 打包进去,而打包模块可以保留,所以 build.gradle 文件配置如下:

android {
    enforceUniquePackageName = false
    packageBuildConfig(false)
    lintOptions {
        abortOnError false
    }
}

参考:
Android多模块构建合并aar解决方案
合并 aar 方案调研
Gradle学习笔记(四)-- fat-aar.gradle解析
解决com.android.dex.DexException: Multiple dex files define L/BuildConfig;
[Android]多module合成单一module技巧

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,544评论 25 707
  • 说明 本文主要介绍和Gradle关系密切、相对不容易理解的配置,偏重概念介绍。部分内容是Android特有的(例如...
    jzj1993阅读 15,352评论 1 62
  • 前言 前段时间,我在实现gradle多模块构建遇到一个问题,以前我们基本上是以jar包形式让开发者集成到工程中使用...
    IT_xiao小巫阅读 19,656评论 18 27
  • 我像个偷月光的影子 悄悄带走了一寸夜的月光 偷偷地,将它埋在心底 嘘~不要让他们知道 影子带走月光的秘密 那是夜里...
    泾木阅读 230评论 0 0
  • “我爱我的妈妈,如此之深。她是个隐君子,她是个酒鬼,她基本失明,她还是精神分裂症患者。但我绝不会忘记她爱我...
    天净树梓阅读 501评论 3 3