本文基于Android Q,介绍将AOSP中的仓库解耦为独立应用时的一些疑难点。
笔者在负责安卓大版本升级时,遇到很多坑,因此把这些疑难点记录下来,一方面是为了今后自己回顾,另一方面也是为了帮助同行业的开发者。
考虑到数据安全,文中内容已做脱敏处理。
下面将介绍如何将AOSP中的一个仓库解耦出来成为一个可独立编译apk的应用。
首先你需要具备以下知识技能:
假设你已经搞定了上述知识点,那么接下来我们将开始一步一步解耦一个独立编译的应用。
待解耦的模块,本身必须是一个git仓库。
AOSP中的编译方式不管整编还是单编,均是通过模块根目录下的编译文件,旧版本上为Android.mk
,近期已经改用Android.bp
,其类似Json格式的编写方式,更利于开发人员阅读和维护。
解耦为独立编译应用时,就需要改为gradle编译的方式,因此首先需要配置build.gradle
文件,其次是本地的properties
文件,以及其他一些配置文件和脚本文件(视自身业务需求而定)。
如果执行gradle编译命令,能开始运行并输出结果,那么继续下一步。
解耦前的模块可能使用到一些资源id,比如依赖框架层的资源id,原先由于位于AOSP环境中,因此在整编或单编时可以直接引用,但解耦出来后会因为找不到资源id而无法通过编译。
对此,解决方案为通过Resources#getIdentifier获取,示例:
原生有一处资源引用为com.android.internal.R.string.resName
,那么解耦后的资源id获取方式为:
int resId = context.getResources().getIdentifier("resName", "string", "android");
由于其最终通过AssetManager#nativeGetResourceIdentifier获得,因此当存在大量资源id时,需要考虑对性能的影响。
一些模块可能使用到源码中没有的java文件,其特征会有proto关键词。这便是动态生成的类。
proto文件是一种用来编译生成java文件的文件,其配置有要生成的java文件的一些信息,包括package,className,innerClass,field等,需要借助google的protobuf
这个库才能编译。
关于protobuf:
AOSP中的模块,如果有proto文件,那么模块本身也是配置了对protobuf
的依赖,因此才能正常执行整编。也就是说,整编的编译顺序中,会先编译各模块的proto文件生成所需的java文件,再开始对AOSP整编,否则工程中有引用这些java文件就编译不过了。
对于解耦出来的应用,不应去使用protobuf
库自主编译生成java文件,而是直接引用整编的编译产物。
在Android.bp
文件中,会定义proto文件相关信息,例如:
java_library {
name: "Demo-proto",
srcs: ["src/**/*.proto"],
proto: {
type: "nano",
}
}
AOSP中proto文件的编译产物位于:
android\out\target\common\obj\JAVA_LIBRARIES
在整编成功后的JAVA_LIBRARIES
文件夹下,就可以找到名为Demo-proto_intermediates
的文件夹,其中便包含了我们所需的java文件。
将其中的classes.jar
重命名为Demo-proto.jar
,复制到解耦后的工程的libs
下并配置好对其的依赖。
通常是codebase未及时更新。
查看Android.bp
文件中是否有定义相关信息, 然后再按照proto的处理方式,将缺失的类通过jar包引入。