1. 什么是 bootjar
要理解 boojar 首先得理解 Java-类的加载机制。
其中最重要的概念是双亲委托加载模式:
从上图我们可以看到 Bootstrap ClassLoader 的加载路径就包含了 BOOTCLASSPATH 路径。BOOTCLASSPATH 是一个环境变量,我们可以这样查看:
1 | pure:/ # echo $BOOTCLASSPATH |
Android 中的 bootjar 其实就是要将某个 jar 包添加到 BOOTCLASSPATH。
2. 什么时候需要添加 boojar
想像一下,你是做 TV 系统的。TV 芯片的供应商有 hisi, rtk, mtk 等,他们在系统中都提供了一套 TV 相关的 java api 给我们应用使用。 我们同时要开发这几个不同芯片供应商的平台。我们的应用应该怎么开发呢?
- 最原始的方案是应用层分别对接不同芯片供应商提供的接口
这种方案的问题是假如有N个应用需要用到 TV 接口的话,那我们就需要维护 3 * N个应用。这种方法的代码维护成本太高了,不可接受。
- 最原始的方案是应用层分别对接不同芯片供应商提供的接口
- 封装一层中间层,我们称之为 middleware(中间件), apk 只对接中间件,平台差异部分由中间来分别对接。
这种方案我们需要维护N个应用 + 三份中间件接口。这种方案有以下好处:- 那所有应用都统一对接到中间件即可。不需要处理平台差异。大大减少应用代码的维护量。
- 供应商接口有变化时,应用层不需要更改。只需要在中间件调整适配即可。
- 封装一层中间层,我们称之为 middleware(中间件), apk 只对接中间件,平台差异部分由中间来分别对接。
这种方案有两种实现方式。
- 中间件 jar 带完整实现,编译 apk 时打包到 apk 中。这种方案的问题是当中间件实现有变化时,所有的 apk 都需要重新打包发布,不好维护。
- 中间件 jar 是空接口。具体实现放到平台中。当中间件实现有变化时,apk 不需要任何更改,只需要更改平台的中间实现就行。要实现这种方案就得通过 bootjar 来实现了。目前我们使用的是这种方案。
3. 添加 bootjar
添加 bootjar 的方法很简单,假设我们有个 java_library api.pure
需要加入 bootjar, 只需要做以下工作即可。
device.mk 中加入
1 | PRODUCT_PACKAGES += api.pure |
Android/build/core/tasks/check_boot_jars/package_whitelist.txt 中加入
1 | api\.pure |
4. Android 10 boojar 的变化
最近在搞 hisiv900, Android 10 的平台。需要把之前的中间导入到系统里面。以前的中间件是 Android.mk 模块。按照上面的方法导入时编译出现类似以下的错误:
1 | [100% 2/2] out/soong/.bootstrap/bin/soong_build out/soong/build.ninja |
只提示找不到 dex jar, 也不知道为什么错了。折腾了好久,后面在 build 目录搜索 PRODUCT_BOOT_JARS 关键字,发现 build/make/core/java.mk 有以下代码:
1 | ifneq ($(filter $(LOCAL_MODULE),$(PRODUCT_BOOT_JARS)),) |
才发现,原来 Android 10 之后,Android.mk 定义的模块是没法加入到 bootjar 了。需要切换成 Android.bp。
经过实验总结,要加入到 bootjar 需要满足以下条件:
Android.bp 模块 + installable 为 true + source file 需要是源文件(不能是jar包)