1. 项目背景与需求分析
在Android Framework开发中,我们经常会遇到需要将Java代码编译成JAR文件并在系统服务中调用的场景。这种需求通常出现在以下几种情况:
- 需要保护核心算法或业务逻辑的闭源实现
- 需要在多个项目中复用相同的功能模块
- 需要将第三方库集成到系统服务中
- 需要动态更新部分功能而不重新编译整个系统
本文将以一个实际的边缘手势功能为例,详细介绍如何在Android Framework中集成和调用由Java编译成的JAR接口。这个功能需要在系统手势监听服务(SystemGesturesPointerEventListener)中调用闭源的边缘手势管理功能。
2. Java代码编译成JAR的两种方法
2.1 手动编译方法
手动编译适合快速验证和小规模项目,但存在依赖管理和兼容性问题,不推荐在生产环境中使用。
2.1.1 创建项目目录结构
首先需要按照Java包名规范创建目录结构:
bash复制mkdir -p EdgeGesturePrivate/src/com/android/server/policy/
这个目录结构对应了我们的Java类包名com.android.server.policy,这是Android系统服务常用的包名规范。
2.1.2 编写Java源代码
创建EdgeGestureArrowManager.java文件,实现边缘手势管理功能:
java复制package com.android.server.policy;
import android.content.Context;
public class EdgeGestureArrowManager {
public EdgeGestureArrowManager(Context context) {
// 初始化逻辑
}
public void systemReady() {
// 系统准备就绪时调用
}
public void onSwipeFromLeftStart(float initialY) {
// 处理从左边缘滑动手势开始
}
public void onSwipeFromLeftCancel() {
// 取消从左边缘滑动手势
}
public void onSwipeFromLeftComplete() {
// 从左边缘滑动手势完成
}
}
2.1.3 获取framework.jar依赖
Android Framework中的类需要依赖framework.jar:
bash复制cp ./out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar \
EdgeGesturePrivate/libs/
注意:不同Android版本路径可能略有不同,Android 8.x通常在这个位置。
2.1.4 编译脚本实现
创建编译脚本cc.sh:
bash复制#!/bin/bash
# 编译为class文件
javac -cp "libs/classes.jar" \
-source 1.8 -target 1.8 \
-d ./classes \
src/com/android/server/policy/EdgeGestureArrowManager.java
# 打包为JAR文件
jar cvf EdgeGestureArrowManager.jar -C classes .
# 验证JAR内容
jar tf EdgeGestureArrowManager.jar
执行脚本后,应该看到类似输出:
code复制META-INF/
META-INF/MANIFEST.MF
com/
com/android/
com/android/server/
com/android/server/policy/
com/android/server/policy/EdgeGestureArrowManager.class
2.2 AOSP编译方法(推荐)
AOSP编译方法能更好地处理依赖关系和兼容性问题,是生产环境推荐的做法。
2.2.1 创建临时源码目录
在AOSP源码树中创建临时目录:
bash复制mkdir -p frameworks/base/libs/EdgeGestureArrowManager-tmp/src/com/android/server/policy/
2.2.2 编写Android.mk
创建编译配置文件:
makefile复制LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := EdgeGestureArrowManager
LOCAL_SRC_FILES := \
src/com/android/server/policy/EdgeGestureArrowManager.java
LOCAL_JAVA_LIBRARIES := \
core-oj \
core-libart \
framework
LOCAL_MODULE_TAGS := optional
LOCAL_CERTIFICATE := platform
# 使用Jack编译器
LOCAL_JACK_ENABLED := incremental
LOCAL_DX_FLAGS := --min-sdk-version=21
include $(BUILD_JAVA_LIBRARY)
2.2.3 编译并获取产物
执行编译命令:
bash复制source build/envsetup.sh
lunch <your_target>
mmm frameworks/base/libs/EdgeGestureArrowManager-tmp/
编译完成后,可以从以下路径获取生成的JAR文件:
code复制out/target/common/obj/JAVA_LIBRARIES/EdgeGestureArrowManager_intermediates/javalib.jar
out/target/common/obj/JAVA_LIBRARIES/EdgeGestureArrowManager_intermediates/classes.jack
3. 在Framework中集成JAR文件
3.1 项目结构规划
我们需要将JAR文件集成到以下位置:
code复制frameworks/base/libs/
├── EdgeGestureArrowManager/ # JAR文件目录
└── EdgeGestureArrowManager.jar
└── Android.mk
frameworks/base/services/core/
├── Android.mk # 修改此文件
└── java/com/android/server/policy/
└── SystemGesturesPointerEventListener.java # 调用JAR的入口
3.2 反射调用封装
为了避免直接依赖JAR中的类,我们使用反射机制进行调用:
java复制public class EdgeGestureHelper {
private static final String TAG = "EdgeGestureHelper";
private static final String CLASS_NAME = "com.android.server.policy.EdgeGestureArrowManager";
private Object mManagerInstance;
private boolean mAvailable = false;
public EdgeGestureHelper(Context context) {
try {
Class<?> clazz = Class.forName(CLASS_NAME);
mManagerInstance = clazz.getConstructor(Context.class).newInstance(context);
mAvailable = true;
} catch (Exception e) {
Slog.w(TAG, "EdgeGesture not available: " + e.getMessage());
}
}
// 通用方法调用封装
private void callMethod(String methodName, Object... args) {
if (!mAvailable) return;
try {
Class<?>[] paramTypes = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg instanceof Integer) {
paramTypes[i] = int.class;
}
// 其他基本类型处理...
else {
paramTypes[i] = arg.getClass();
}
}
mManagerInstance.getClass()
.getMethod(methodName, paramTypes)
.invoke(mManagerInstance, args);
} catch (Exception e) {
Slog.e(TAG, "Failed to call " + methodName, e);
}
}
}
3.3 Android.mk配置
3.3.1 JAR模块配置
在frameworks/base/libs/EdgeGestureArrowManager/Android.mk中:
makefile复制LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := EdgeGestureArrowManager
LOCAL_SRC_FILES := EdgeGestureArrowManager.jar
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_MODULE_TAGS := optional
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_SUFFIX := .jar
# 生成classes.jar规则
intermediates.COMMON := $(call local-intermediates-dir,COMMON)
$(intermediates.COMMON)/classes.jar: $(LOCAL_PATH)/$(LOCAL_SRC_FILES)
$(copy-file-to-target)
# 生成classes.jack规则
intermediates.COMMONJACK := $(call local-intermediates-dir,COMMONJACK)
$(intermediates.COMMONJACK)/classes.jack: $(LOCAL_PATH)/classes.jack
$(copy-file-to-target)
include $(BUILD_PREBUILT)
3.3.2 服务模块配置
在frameworks/base/services/core/Android.mk中添加依赖:
makefile复制LOCAL_STATIC_JAVA_LIBRARIES += EdgeGestureArrowManager
注意:如果编译报错提示找不到classes.jack,需要确保之前通过AOSP编译生成了这个文件并放在了正确位置。
4. 常见问题与解决方案
4.1 ClassNotFoundException问题
问题现象:
code复制EdgeGesture not available: com.android.server.policy.EdgeGestureArrowManager
解决方案:
- 检查JAR文件是否包含目标类:
bash复制
jar tf EdgeGestureArrowManager.jar | grep EdgeGestureArrowManager - 确保JAR文件在编译时被正确包含:
- 检查
LOCAL_STATIC_JAVA_LIBRARIES配置 - 确保JAR模块的Android.mk正确
- 检查
- 验证classes.jack文件是否存在
4.2 Jack编译器相关问题
问题现象:
code复制ninja: error: '.../classes.jack', needed by '.../services.core_intermediates/classes.jack', missing
解决方案:
- 确保使用AOSP编译生成了classes.jack文件
- 在JAR模块的Android.mk中添加jack文件处理规则
- 或者禁用Jack编译器:
makefile复制
LOCAL_JACK_ENABLED := disabled
4.3 Proguard混淆问题
如果需要混淆JAR代码,可以修改Android.mk:
makefile复制# 禁用Jack,使用DX
LOCAL_JACK_ENABLED := disabled
# 启用Proguard
LOCAL_PROGUARD_ENABLED := obfuscation
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
创建proguard.flags配置文件:
code复制-keep public class com.android.server.policy.EdgeGestureArrowManager {
public <methods>;
}
5. 实际应用示例
在SystemGesturesPointerEventListener中使用封装好的Helper:
java复制public class SystemGesturesPointerEventListener {
private final EdgeGestureHelper mEdgeHelper;
public SystemGesturesPointerEventListener(Context context) {
mEdgeHelper = new EdgeGestureHelper(context);
}
public void onPointerEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (event.getX() < 50) { // 左边缘滑动
mEdgeHelper.onSwipeFromLeftStart(event.getY());
}
break;
case MotionEvent.ACTION_UP:
mEdgeHelper.onSwipeFromLeftComplete();
break;
case MotionEvent.ACTION_CANCEL:
mEdgeHelper.onSwipeFromLeftCancel();
break;
}
}
}
6. 性能优化建议
- 反射缓存:将反射获取的Method对象缓存起来,避免每次调用都查找
- 异步调用:耗时操作应该放在后台线程执行
- 异常处理:完善错误处理和降级方案
- 资源释放:在适当的时候释放资源
优化后的反射调用示例:
java复制private Method mSystemReadyMethod;
private Method mOnSwipeStartMethod;
// 在初始化时缓存Method对象
public EdgeGestureHelper(Context context) {
try {
Class<?> clazz = Class.forName(CLASS_NAME);
mManagerInstance = clazz.getConstructor(Context.class).newInstance(context);
// 缓存常用方法
mSystemReadyMethod = clazz.getMethod("systemReady");
mOnSwipeStartMethod = clazz.getMethod("onSwipeFromLeftStart", float.class);
mAvailable = true;
} catch (Exception e) {
Slog.w(TAG, "EdgeGesture not available", e);
}
}
public void systemReady() {
if (!mAvailable) return;
try {
mSystemReadyMethod.invoke(mManagerInstance);
} catch (Exception e) {
Slog.e(TAG, "Failed to call systemReady", e);
}
}
7. 兼容性考虑
-
Android版本适配:
- 不同Android版本对JAR加载机制可能有差异
- 特别是Android 8.0引入的Jack编译器与后续版本使用的D8/R8
-
API级别检查:
java复制if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // 使用新API } else { // 回退方案 } -
多架构支持:
- 如果JAR包含native代码,需要确保支持所有ABI
- 在Android.mk中配置:
makefile复制
LOCAL_MULTILIB := both
8. 测试验证方法
-
单元测试:
java复制@Test public void testEdgeGestureHelper() { EdgeGestureHelper helper = new EdgeGestureHelper(mock(Context.class)); assertTrue(helper.isAvailable()); helper.systemReady(); // 验证预期行为 } -
集成测试:
- 验证JAR是否被正确打包进系统镜像
- 验证系统服务是否能正常调用JAR功能
-
运行时检查:
bash复制
adb shell dumpsys package <your_package> | grep EdgeGesture
9. 扩展应用场景
这种JAR集成方式不仅适用于边缘手势功能,还可以应用于:
- 系统定制功能:添加厂商特定的系统行为
- 闭源算法集成:保护核心算法知识产权
- 动态功能模块:通过替换JAR实现功能更新
- 跨项目复用:将通用功能打包为JAR供多个项目使用
10. 安全注意事项
-
签名验证:确保JAR使用平台签名
makefile复制
LOCAL_CERTIFICATE := platform -
权限控制:在AndroidManifest.xml中声明必要权限
-
输入验证:对所有外部输入进行严格验证
-
日志过滤:避免在日志中泄露敏感信息
通过以上完整的方案,我们可以在Android Framework中安全、高效地集成和调用由Java编译成的JAR接口,实现功能的模块化和闭源保护。