Android系统开发入门-16.Android系统添加 bootjar

1. 什么是 bootjar

要理解 boojar 首先得理解 Java-类的加载机制
其中最重要的概念是双亲委托加载模式:
双亲委托加载

从上图我们可以看到 Bootstrap ClassLoader 的加载路径就包含了 BOOTCLASSPATH 路径。BOOTCLASSPATH 是一个环境变量,我们可以这样查看:

1
2
3
pure:/ # echo $BOOTCLASSPATH
/apex/com.android.runtime/javalib/core-oj.jar:/apex/com.android.runtime/javalib/core-libart.jar:/apex/com.android.runtime/javalib/okhttp.jar:/apex/com.android.runtime/javalib/bouncycastle.jar:/apex/com.android.runtime/javalib/apache-xml.jar:/system/framework/framework.jar:/system/framework/ext.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/system/framework/android.test.base.jar:/apex/com.android.conscrypt/javalib/conscrypt.jar:/apex/com.android.media/javalib/updatable-media.jar
pure:/ #

Android 中的 bootjar 其实就是要将某个 jar 包添加到 BOOTCLASSPATH。

2. 什么时候需要添加 boojar

想像一下,你是做 TV 系统的。TV 芯片的供应商有 hisi, rtk, mtk 等,他们在系统中都提供了一套 TV 相关的 java api 给我们应用使用。 我们同时要开发这几个不同芯片供应商的平台。我们的应用应该怎么开发呢?

    1. 最原始的方案是应用层分别对接不同芯片供应商提供的接口
      这种方案的问题是假如有N个应用需要用到 TV 接口的话,那我们就需要维护 3 * N个应用。这种方法的代码维护成本太高了,不可接受。
    1. 封装一层中间层,我们称之为 middleware(中间件), apk 只对接中间件,平台差异部分由中间来分别对接。
      这种方案我们需要维护N个应用 + 三份中间件接口。这种方案有以下好处:
      • 那所有应用都统一对接到中间件即可。不需要处理平台差异。大大减少应用代码的维护量。
      • 供应商接口有变化时,应用层不需要更改。只需要在中间件调整适配即可。

这种方案有两种实现方式。
- 中间件 jar 带完整实现,编译 apk 时打包到 apk 中。这种方案的问题是当中间件实现有变化时,所有的 apk 都需要重新打包发布,不好维护。
- 中间件 jar 是空接口。具体实现放到平台中。当中间件实现有变化时,apk 不需要任何更改,只需要更改平台的中间实现就行。要实现这种方案就得通过 bootjar 来实现了。目前我们使用的是这种方案。

3. 添加 bootjar

添加 bootjar 的方法很简单,假设我们有个 java_library api.pure 需要加入 bootjar, 只需要做以下工作即可。
device.mk 中加入

1
2
PRODUCT_PACKAGES += api.pure
PRODUCT_BOOT_JARS += api.pure

Android/build/core/tasks/check_boot_jars/package_whitelist.txt 中加入

1
2
api\.pure
api\.pure\..*

4. Android 10 boojar 的变化

最近在搞 hisiv900, Android 10 的平台。需要把之前的中间导入到系统里面。以前的中间件是 Android.mk 模块。按照上面的方法导入时编译出现类似以下的错误:

1
2
3
4
5
6
7
8
9
10
[100% 2/2] out/soong/.bootstrap/bin/soong_build out/soong/build.ninja
FAILED: out/soong/build.ninja
out/soong/.bootstrap/bin/soong_build -t -l out/.module_paths/Android.bp.list -b out/soong -n out -d out/soong/build.ninja.d -globFile out/soong/.bootstrap/build-globs.ninja -o out/soong/build.ninja Android.bp
internal error: failed to find dex jar path for module "api.pure"
internal error: failed to find dex jar path for module "api.pure"
15:18:21 soong bootstrap failed with: exit status 1

#### failed to build some targets (15 seconds) ####

qiushao@qiushao-pc:/media/qiushao/source-code/android-10.0.0_r33$

只提示找不到 dex jar, 也不知道为什么错了。折腾了好久,后面在 build 目录搜索 PRODUCT_BOOT_JARS 关键字,发现 build/make/core/java.mk 有以下代码:

1
2
3
ifneq ($(filter $(LOCAL_MODULE),$(PRODUCT_BOOT_JARS)),)
$(call pretty-error,Modules in PRODUCT_BOOT_JARS must be defined in Android.bp files)
endif

才发现,原来 Android 10 之后,Android.mk 定义的模块是没法加入到 bootjar 了。需要切换成 Android.bp。
经过实验总结,要加入到 bootjar 需要满足以下条件:
Android.bp 模块 + installable 为 true + source file 需要是源文件(不能是jar包)