1. 理解Android.mk在AOSP编译体系中的角色
在AOSP(Android Open Source Project)的编译系统中,Android.mk文件扮演着构建脚本的核心角色。这个基于Makefile语法的配置文件,定义了模块的编译规则和依赖关系。对于Android 9(Pie)版本而言,虽然Google已经开始推广Soong构建系统(使用Blueprint和.bp文件),但Android.mk仍然是大量遗留模块和第三方代码的主要构建方式。
当我们需要在AOSP中引入第三方JAR包时,Android.mk提供了几种不同的集成方式。最常见的情况包括:
- 将预编译的JAR包作为静态依赖引入
- 将JAR包中的类文件编译进系统镜像
- 将JAR包作为共享库提供给其他模块使用
在Android 9的环境下,我们需要特别注意几个版本特性:
- 隐藏API限制:从Android 9开始,Google加强了对非SDK接口的限制,这意味着即使某些类在JAR包中存在,也可能因为访问限制而无法正常使用。
- 模块签名:系统应用的签名验证更加严格,引入的JAR包可能需要重新签名。
- 类加载机制:Android 9优化了类加载顺序,这对依赖JAR包中资源的应用有影响。
提示:在开始修改前,建议先通过
make update-api命令更新API检查,这能帮助发现潜在的兼容性问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 准备编译环境与基础配置
2.1 搭建Android 9编译环境
在开始修改Android.mk前,我们需要确保基础编译环境正确设置。以下是Android 9推荐的开发环境配置:
bash复制# 安装JDK(注意Android 9需要Java 8)
sudo apt-get install openjdk-8-jdk
# 安装其他依赖
sudo apt-get install git-core gnupg flex bison gperf build-essential \
zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 \
lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache \
libgl1-mesa-dev libxml2-utils xsltproc unzip
2.2 获取AOSP源码
建议使用清华镜像源加速下载:
bash复制repo init -u https://mirrors.tuna.tsinghua.edu.cn/git/AOSP/platform/manifest -b android-9.0.0_rXX
repo sync -j4
2.3 理解模块目录结构
典型的包含JAR包的模块目录结构如下:
code复制vendor/example/mylib/
├── Android.mk
├── libs/
│ ├── mylib.jar
└── src/
└── com/example/mylib/
└── MyClass.java
其中libs/目录存放预编译的JAR包,src/目录可选,用于存放可能需要与JAR包一起编译的源代码。
3. Android.mk集成JAR包的多种方式
3.1 基础集成方法
最简单的集成方式是使用LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES变量:
makefile复制LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# 定义模块名称(需与JAR文件名对应)
LOCAL_MODULE := mylib-prebuilt
LOCAL_SRC_FILES := libs/mylib.jar
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_MODULE_SUFFIX := .jar
LOCAL_MODULE_TAGS := optional
# 声明为预编译库
include $(BUILD_PREBUILT)
# 在其他模块中引用
include $(CLEAR_VARS)
LOCAL_MODULE := myapp
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := mylib-prebuilt
include $(BUILD_PACKAGE)
3.2 高级配置选项
对于更复杂的需求,可以使用以下配置:
makefile复制# 多JAR包集成
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
lib1:libs/lib1.jar \
lib2:libs/lib2.jar
# 排除JAR包中的某些类
LOCAL_JAR_EXCLUDE_FILES := com/example/Test.class
# 包含原生库(针对包含JNI的JAR包)
LOCAL_JNI_SHARED_LIBRARIES := libnativecode
# 资源文件处理(针对包含资源的JAR包)
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
3.3 系统级集成
如果JAR包需要被多个系统组件使用,可以将其安装到系统分区:
makefile复制include $(CLEAR_VARS)
LOCAL_MODULE := framework-mylib
LOCAL_SRC_FILES := libs/mylib.jar
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_MODULE_TAGS := optional
LOCAL_INSTALLED_MODULE_STEM := mylib.jar
LOCAL_MODULE_PATH := $(TARGET_OUT_JAVA_LIBRARIES)
include $(BUILD_PREBUILT)
然后在框架配置中声明:
makefile复制PRODUCT_BOOT_JARS += framework-mylib
4. 常见问题排查与解决方案
4.1 类加载失败问题
现象:ClassNotFoundException或NoClassDefFoundError
排查步骤:
- 检查JAR包是否确实包含所需的类文件:
bash复制jar tf libs/mylib.jar | grep "ClassName" - 确认模块依赖关系正确,使用
mm命令单独编译模块后检查生成的APK:bash复制unzip -l out/target/product/xxx/system/app/MyApp/MyApp.apk | grep "ClassName" - 检查是否因ProGuard优化导致类被移除
解决方案:
- 在
proguard.flags中添加保持规则:code复制-keep class com.example.** { *; } - 确保
LOCAL_PROGUARD_ENABLED设置为full或custom
4.2 版本冲突问题
现象:java.lang.SecurityException或方法签名不匹配
排查步骤:
- 使用
dexdump查看JAR包中的API版本:bash复制dexdump -f libs/mylib.jar | grep "api_level" - 检查依赖树:
bash复制
./gradlew dependencies > dep.txt
解决方案:
- 在Android.mk中排除冲突依赖:
makefile复制
LOCAL_JAVA_LIBRARIES := core-oj core-libart okhttp - 使用
LOCAL_JAVA_LIBRARY_OVERLAYS指定优先级
4.3 资源ID冲突问题
现象:资源找不到或显示错误内容
排查步骤:
- 检查合并后的资源表:
bash复制
aapt dump resources out/target/product/xxx/system/app/MyApp/MyApp.apk - 确认资源前缀设置:
makefile复制LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res LOCAL_AAPT_FLAGS += --auto-add-overlay
解决方案:
- 为模块添加资源前缀:
makefile复制
LOCAL_AAPT_FLAGS += --rename-manifest-package com.example.newpackage - 使用
--extra-packages标记:makefile复制
LOCAL_AAPT_FLAGS += --extra-packages com.example.mylib
5. 性能优化与进阶技巧
5.1 编译加速方案
对于大型JAR包,可以采用以下优化手段:
-
预编译dex:
makefile复制
LOCAL_DEX_PREOPT := true LOCAL_DEX_PREOPT_FLAGS := --compiler-filter=quicken -
使用JAR索引:
makefile复制LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt -
模块化编译:
makefile复制
LOCAL_STATIC_JAVA_AAR_LIBRARIES := lib1 lib2 LOCAL_USE_AAPT2 := true
5.2 安全加固措施
-
代码混淆:
makefile复制
LOCAL_PROGUARD_FLAG_FILES := proguard.flags LOCAL_PROGUARD_ENABLED := full -
签名验证:
makefile复制
LOCAL_CERTIFICATE := platform LOCAL_PRIVILEGED_MODULE := true -
权限控制:
makefile复制
LOCAL_REQUIRED_MODULES := privapp-permissions-com.example.app.xml
5.3 调试技巧
-
查看编译中间结果:
bash复制ls -l out/target/common/obj/JAVA_LIBRARIES/mylib_intermediates/ -
生成依赖图:
bash复制make deps-license PROD=true > deps.html -
单模块编译验证:
bash复制mm -B | tee build.log
6. 实际案例:WebView兼容包集成
以集成兼容性WebView包为例,展示完整流程:
-
准备JAR包:
- 将
webview_compat.jar放入vendor/google/libs/
- 将
-
编写Android.mk:
makefile复制LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := webview-compat LOCAL_SRC_FILES := libs/webview_compat.jar LOCAL_MODULE_CLASS := JAVA_LIBRARIES LOCAL_MODULE_TAGS := optional LOCAL_MODULE_SUFFIX := .jar include $(BUILD_PREBUILT) -
系统集成:
makefile复制
PRODUCT_PACKAGES += webview-compat PRODUCT_BOOT_JARS += webview-compat -
应用集成:
makefile复制
LOCAL_STATIC_JAVA_LIBRARIES := webview-compat -
版本检查:
java复制if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { WebViewCompat.loadLibrary(); }
这个案例展示了如何为不同Android版本提供兼容性支持,同时保持代码的统一性。
