文章目录
- 把java代码编译成jar
-
- [1. 创建目录](#1. 创建目录)
- [2. 创建java代码](#2. 创建java代码)
- [3. 从AOSP安卓源码中找到framework.jar](#3. 从AOSP安卓源码中找到framework.jar)
- [4. 编译生成jar](#4. 编译生成jar)
- AOSP编译(推荐)
- 初步方案如下
-
- [1. 想法](#1. 想法)
- [2. 创建libs目录并放入JAR](#2. 创建libs目录并放入JAR)
- [3. 反射调用](#3. 反射调用)
- [4. 修改Android.mk](#4. 修改Android.mk)
- 使用LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES(失败)
- 使用LOCAL_STATIC_JAVA_LIBRARIES
- AOSP方法编译(详细方案)
- proguard混淆加密
- Android相关专栏
把java代码编译成jar
1. 创建目录
bash
创建一个临时项目目录
mkdir EdgeGesturePrivate
cd EdgeGesturePrivate
# 要根据包名来创建文件夹
mkdir src/com/android/server/policy/
# 然后在此目录下创建EdgeGestureArrowManager.java
2. 创建java代码
java源码
java
package com.android.server.policy;
public class EdgeGestureArrowManager {
public EdgeGestureArrowManager(Context context) {
}
public void systemReady() {
}
public void onSwipeFromLeftStart(float initialY) {
}
public void onSwipeFromLeftCancel() {
}
public void onSwipeFromLeftComplete() {
}
}
3. 从AOSP安卓源码中找到framework.jar
Android8一般是这个文件:
text
./out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar
把它拷贝到上面创建的目录EdgeGesturePrivate中的libs目录下,如果没有这个jar后面手动编译java会报错。
4. 编译生成jar
手动编译
注意此方法只是做个简单示范,实际后面操作起来会比较复杂,不建议使用(后面会介绍如何在AOSP androd sdk源码中编译,且推荐用这种方法)
shell
# 编译为JAR
javac -cp "libs/classes.jar" \
-source 1.8 -target 1.8 \
-d ./classes \
src/com/android/server/policy/EdgeGestureArrowManager.java
# 打包
jar cvf EdgeGestureArrowManager.jar -C classes
执行后,不报错情况下会生成EdgeGestureArrowManager.jar。
执行以下指令,验证生成的jar是否正常:
bash
jar tf EdgeGestureArrowManager.jar
正常会输出类似如下的内容:
text
META-INF/
META-INF/MANIFEST.MF
com/
com/android/
.....
AOSP编译(推荐)
此方法能应对各种报错,推荐用这种方法,具体使用后面会详细说明,这只省略。
初步方案如下
1. 想法
想在frameworks/base/services/core/java/com/android/server/policy/SystemGesturesPointerEventListener.java中调用jar中的函数,
可以直接在frameworks/base/services/core/下修改,把jar集成到安卓代码中。
Android SDK中
text
frameworks/base/libs
├── EdgeGestureArrowManager/ # 新建这个目录存放闭源JAR
└── EdgeGestureArrowManager.jar
frameworks/base/services/core/
├── Android.mk # 修改这个文件
└── java/com/android/server/policy/
└── SystemGesturesPointerEventListener.java # 在这里面调用jar中的函数
2. 创建libs目录并放入JAR
bash
# 1. 创建libs目录
mkdir -p frameworks/base/libs/EdgeGestureArrowManager
# 2. 复制你的JAR文件
cp /path/to/EdgeGestureArrowManager.jar frameworks/base/libs/EdgeGestureArrowManager/
3. 反射调用
EdgeGestureHelper.java封装了对jar的反射调用接口
java
package com.android.server.policy;
import android.content.Context;
import android.util.Slog;
import android.util.Log;
/**
* 边缘手势帮助类 - 直接反射调用闭源代码
*/
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;
Slog.i(TAG, "EdgeGesture loaded");
Log.d(TAG, "----------EdgeGesture loaded");
} catch (Exception e) {
Slog.w(TAG, "EdgeGesture not available: " + e.getMessage());
}
}
public void systemReady() {
Log.d(TAG, "----------systemReady");
callMethod("systemReady");
}
public void onSwipeFromLeftStart(float initialY) {
Log.d(TAG, "------------onSwipeFromLeftStart");
callMethod("onSwipeFromLeftStart", float.class, initialY);
}
public void onSwipeFromLeftCancel() {
Log.d(TAG, "-------------onSwipeFromLeftCancel");
callMethod("onSwipeFromLeftCancel");
}
public void onSwipeFromLeftComplete() {
Log.d(TAG, "---------onSwipeFromLeftComplete");
callMethod("onSwipeFromLeftComplete");
}
private void callMethod(String methodName, Object... args) {
if (!mAvailable || mManagerInstance == null) return;
try {
Class<?>[] paramTypes = new Class<?>[args.length];
Object[] actualArgs = new Object[args.length];
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
actualArgs[i] = arg;
// 获取实际参数类型
if (arg instanceof Integer) {
paramTypes[i] = int.class;
} else if (arg instanceof Float) {
paramTypes[i] = float.class;
} else if (arg instanceof Double) {
paramTypes[i] = double.class;
} else if (arg instanceof Boolean) {
paramTypes[i] = boolean.class;
} else if (arg instanceof Long) {
paramTypes[i] = long.class;
} else if (arg instanceof Short) {
paramTypes[i] = short.class;
} else if (arg instanceof Byte) {
paramTypes[i] = byte.class;
} else if (arg instanceof Character) {
paramTypes[i] = char.class;
} else {
// 对于非基本类型,使用实际的类
paramTypes[i] = arg.getClass();
}
}
mManagerInstance.getClass()
.getMethod(methodName, paramTypes)
.invoke(mManagerInstance, actualArgs);
} catch (Exception e) {
Slog.e(TAG, "Failed to call " + methodName, e);
Log.e(TAG, "--------Failed to call " + methodName, e);
}
}
// 重载方法,允许显式指定参数类型
private void callMethod(String methodName, Class<?> paramType, Object arg) {
if (!mAvailable || mManagerInstance == null) return;
try {
mManagerInstance.getClass()
.getMethod(methodName, paramType)
.invoke(mManagerInstance, arg);
} catch (Exception e) {
Slog.e(TAG, "Failed to call " + methodName, e);
Log.e(TAG, "--------Failed to call " + methodName, e);
}
}
public boolean isAvailable() {
return mAvailable;
}
}
然后在 SystemGesturesPointerEventListener 中就可以通过EdgeGestureHelper调用到jar中的功能了:
java
public class SystemGesturesPointerEventListener {
private EdgeGestureHelper mEdgeHelper;
public SystemGesturesPointerEventListener(Context context) {
mEdgeHelper = new EdgeGestureHelper(context);
}
public void onPointerEvent(MotionEvent event) {
// ... 手势检测逻辑
if (event.getAction() == MotionEvent.ACTION_DOWN && event.getX() < 50) {
mEdgeHelper.callSwipeFromLeftStart(event.getY());
}
if (event.getAction() == MotionEvent.ACTION_UP) {
mEdgeHelper.callSwipeFromLeftComplete();
}
// 系统就绪时调用
mEdgeHelper.callSystemReady();
}
}
4. 修改Android.mk
要在frameworks/base/services/core/子目录中的java去调用我们的jar,所以我们修改frameworks/base/services/core/Android.mk
使用LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES(失败)
makefile
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := services.core
LOCAL_SRC_FILES := $(call all-java-files-under,java)
# 使用 LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := EdgeGestureArrowManager:../libs/EdgeGestureArrowManager/EdgeGestureArrowManager.jar
# 其他配置保持原样...
LOCAL_JAVA_LIBRARIES := \
services.net \
android.hardware.light-V2.0-java \
android.hardware.power-V1.0-java \
android.hardware.tv.cec-V1.0-java \
android.hidl.manager-V1.0-java
LOCAL_STATIC_JAVA_LIBRARIES += \
time_zone_distro \
time_zone_distro_installer \
android.hidl.base-V1.0-java-static \
android.hardware.weaver-V1.0-java-static \
android.hardware.biometrics.fingerprint-V2.1-java-static \
android.hardware.oemlock-V1.0-java-static \
android.hardware.tetheroffload.control-V1.0-java-static \
android.hardware.vibrator-V1.0-java-constants \
android.hardware.configstore-V1.0-java-static
# 保持其他配置不变...
include $(BUILD_STATIC_JAVA_LIBRARY)
注意,这里如果把
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := EdgeGestureArrowManager:.../libs/EdgeGestureArrowManager/EdgeGestureArrowManager.jar
改成
LOCAL_STATIC_JAVA_LIBRARIES := EdgeGestureArrowManager:libs/EdgeGestureArrowManager.jar
会报错:
text
ninja: error: 'out/target/common/obj/JAVA_LIBRARIES/services.core_intermediates/classes.jar', needed by 'out/target/common/obj/JAVA_LIBRARIES/services_intermediates/classes-full-debug.jar', missing and no known rule to make it
使用LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES编译不报错,但是烧录到设备中,调用时会报错:
text
EdgeGesture not available: com.android.server.policy.EdgeGestureArrowManager
这个错误说明SystemUI找不到类,后面发现修改路径,比如改成EdgeGestureArrowManager:xxx/EdgeGestureArrowManager.jar,编译也不会报错,判断LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES这个方法不行,起码在Android8.1上行不通。
使用LOCAL_STATIC_JAVA_LIBRARIES
-
LOCAL_STATIC_JAVA_LIBRARIES
删除LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES这行,改成在LOCAL_STATIC_JAVA_LIBRARIES += 后面添加LOCAL_STATIC_JAVA_LIBRARIES := EdgeGestureArrowManager
改法如图:

另外因为jar是要被framework其它代码调用的,所以修改Android.mk时,要把我们的jar写在前面,否则编译会报错:
ninja: error: 'out/target/common/obj/JAVA_LIBRARIES/EdgeGestureArrowManager_intermediates/classes.jar', needed by 'out/target/common/obj/JAVA_LIBRARIES/services.core_intermediates/classes-full-debug.jar', missing and no known rule to make it
- 创建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
编译报错:
ninja: error: 'out/target/common/obj/JAVA_LIBRARIES/EdgeGestureArrowManager_intermediates/classes.jar', needed by 'out/target/common/obj/JAVA_LIBRARIES/services.core_intermediates/classes-full-debug.jar', missing and no known rule to make it
这里缺少classes.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)
# 定义如何从预构建JAR创建classes.jar
$(intermediates.COMMON)/classes.jar: $(LOCAL_PATH)/$(LOCAL_SRC_FILES) | $(ACP)
@echo "Copying $< to $@"
$(copy-file-to-target)
# 设置中间文件
ALL_MODULES.$(LOCAL_MODULE).CLASS.JAR := $(intermediates.COMMON)/classes.jar
ALL_MODULES.$(LOCAL_MODULE).PROTO_CLASSES.JAR := $(intermediates.COMMON)/classes.jar
# 预构建规则
$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/$(LOCAL_SRC_FILES)
@mkdir -p $(dir $@)
$(copy-file-to-target)
include $(BUILD_PREBUILT)
这样改完编译会报另外的错误
ninja: error: 'out/target/common/obj/JAVA_LIBRARIES/EdgeGestureArrowManager_intermediates/classes.jack', needed by 'out/target/common/obj/JAVA_LIBRARIES/services.core_intermediates/classes.jack', missing and no known rule to make it
这个错误表明AOSP正在使用Jack编译器(Java 8支持),而预构建的JAR没有相应的.jack文件。
AOSP方法编译(详细方案)
先让AOSP编译源代码生成JAR,然后移除源代码,只使用JAR文件
第一步:临时源代码编译
首先创建源代码目录结构:
bash
mkdir -p frameworks/base/libs/EdgeGestureArrowManager-tmp/src/com/android/server/policy/
将EdgeGestureArrowManager.java复制到该目录:
bash
cp /path/to/your/EdgeGestureArrowManager.java frameworks/base/libs/EdgeGestureArrowManager-tmp/src/com/android/server/policy/
创建临时的Android.mk文件:
临时编译EdgeGestureArrowManager
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
# 让AOSP使用Jack编译器
LOCAL_JACK_ENABLED := incremental
LOCAL_DX_FLAGS := --min-sdk-version=21
include $(BUILD_JAVA_LIBRARY)
保存为 frameworks/base/libs/EdgeGestureArrowManager-tmp/Android.mk
第二步:编译获取JAR
bash
source build/envsetup.sh
lunch xxx(这里不同项目不一样)
# 编译临时模块获取JAR
mmm frameworks/base/libs/EdgeGestureArrowManager-tmp/
# 从编译输出中找到生成的JAR文件
# 通常在: out/target/common/obj/JAVA_LIBRARIES/EdgeGestureArrowManager_intermediates/classes.jar
第三步:提取jar与jack
上面编译生成的文件:

bash
# 复制编译好的JAR文件
cp out/target/common/obj/JAVA_LIBRARIES/EdgeGestureArrowManager_intermediates/javalib.jar frameworks/base/libs/EdgeGestureArrowManager/EdgeGestureArrowManager.jar
# 注意会生成这个jar,但是要用的不是这个文件./out/target/product/P931/system/framework/EdgeGestureArrowManager.jar
cp out/target/common/obj/JAVA_LIBRARIES/EdgeGestureArrowManager_intermediates/classes.jack frameworks/base/libs/EdgeGestureArrowManager/
bash
unzip -l out/target/common/obj/JAVA_LIBRARIES/EdgeGestureArrowManager_intermediates/javalib.jar
正确情况下会输出如下,包含META-INF,classes.dex等文件
text
Archive: out/target/common/obj/JAVA_LIBRARIES/EdgeGestureArrowManager-temp_intermediates/javalib.jar
Length Date Time Name
--------- ---------- ----- ----
0 2008-01-01 00:00 META-INF/
64 2008-01-01 00:00 META-INF/MANIFEST.MF
17660 2008-01-01 00:00 classes.dex
--------- -------
17724 3 files
删除源代码文件:
bash
rm -rf frameworks/base/libs/EdgeGestureArrowManager-tmp
# 删除编译生成的临时文件
find out/ -name "EdgeGestureArrowManager*" -exec rm -r {} \;
注意上面是删除了EdgeGestureArrowManager-tmp,之前放jar的EdgeGestureArrowManager目录是不动的,
现在要解决jack报错,需要修改
frameworks/base/libs/EdgeGestureArrowManager/Android.mk
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)
# 定义如何从预构建JAR创建classes.jar
$(intermediates.COMMON)/classes.jar: $(LOCAL_PATH)/$(LOCAL_SRC_FILES) | $(ACP)
@echo "Copying $< to $@"
$(copy-file-to-target)
# 设置中间文件
ALL_MODULES.$(LOCAL_MODULE).CLASS.JAR := $(intermediates.COMMON)/classes.jar
ALL_MODULES.$(LOCAL_MODULE).PROTO_CLASSES.JAR := $(intermediates.COMMON)/classes.jar
# 预构建规则
$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/$(LOCAL_SRC_FILES)
@mkdir -p $(dir $@)
$(copy-file-to-target)
# 创建一个规则来生成classes.jack
intermediates.COMMONJACK := $(call local-intermediates-dir,COMMONJACK)
$(intermediates.COMMONJACK)/classes.jack: $(LOCAL_PATH)/classes.jack | $(ACP)
@echo "Copying $< to $@"
$(copy-file-to-target)
# 设置中间文件
ALL_MODULES.$(LOCAL_MODULE).CLASS.JACK := $(intermediates.COMMONJACK)/classes.jack
ALL_MODULES.$(LOCAL_MODULE).PROTO_CLASSES.JACK:= $(intermediates.COMMONJACK)/classes.jack
# 预构建规则
$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/$(LOCAL_SRC_FILES)
@mkdir -p $(dir $@)
$(copy-file-to-target)
include $(BUILD_PREBUILT)
第五步:更新服务依赖
这个之前有介绍过具体的改法,就是
在 frameworks/base/services/core/Android.mk 中添加:
makefile
LOCAL_STATIC_JAVA_LIBRARIES += EdgeGestureArrowManager
第六步:验证集成
# 编译服务模块
make libandroid_servers -j16
这样就完成了整个流程:先用AOSP编译源代码得到JAR文件,然后移除源代码,只使用JAR进行后续的集成。这样既保持了闭源的要求,又解决了编译的问题。
这样就能获得正确的.jack文件,然后可以使用BUILD_JAVA_LIBRARY来集成它。

```bash
# 检查services模块是否包含EdgeGestureArrowManager类
unzip -l out/target/common/obj/JAVA_LIBRARIES/services_intermediates/classes.jar | grep EdgeGesture

在out目录下会找到这些编译的中间文件

这样再编译整个系统,烧录到设备中,测试代码调用正常了。

proguard混淆加密
低版本的Android可能无法使用proguard混淆,这里只是为了适配高版本Android及为了低版本的失败做验证。
首先存放java的这个EdgeGestureArrowManager-tmp需要还原回去,要在这个目录下操作,
proguard,可以自行到github上下载
https://github.com/Guardsquare/proguard/releases

解压后,把里面的proguard.jar把放到EdgeGestureArrowManager-tmp/libs下
重新编译jar
bash
source build/envsetup.sh
lunch xxx(这里不同项目不一样)
# 编译临时模块获取JAR
mmm frameworks/base/libs/EdgeGestureArrowManager-tmp/
编完后,检查下混淆是否生效,
bash
ls -la out/target/common/obj/JAVA_LIBRARIES/EdgeGestureArrowManager-tmp_intermediates/ | grep proguard
上面指令输出为空,最后发现Android8用jack编译情况Proguard未生效。
据说Jack编译器有自己的混淆机制,LOCAL_PROGUARD_ENABLED 可能被忽略。
尝试显式启用Jack混淆
在Android.mk中添加:
makefile
# 显式启用Jack混淆
LOCAL_JACK_FLAGS += --obfuscate
最后编译出来的,发现没有被混淆,可能Jack不支持混淆,使用另外的思路,关闭Jack,开启Dx并使用Proguard混淆。
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 := disabled
# 启用Proguard混淆
LOCAL_PROGUARD_ENABLED := obfuscation
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
include $(BUILD_JAVA_LIBRARY)
安装需要的desugar,dx
bash
m desugar
m dx
再编译EdgeGestureArrowManager,如下图编译成功,并开启Proguard了:


解压jar,得到classes.dex,再搜索其中的类名关键字EdgeGesture(这个类名由java代码定的,请自行根据自己的类变更):
bash
unzip -p unzip -p out/target/common/obj/JAVA_LIBRARIES/EdgeGestureArrowManager_intermediates/javalib.jar classes.dex > /tmp/classes.dex
strings /tmp/classes.dex | grep -i "EdgeGesture"
如果没有输出,则说明混淆成功了。
把编译成功的jar拷贝出来,并删除编译的中间文件,及java源码
bash
cp out/target/common/obj/JAVA_LIBRARIES/EdgeGestureArrowManager-tmp_intermediates/javalib.jar frameworks/base/libs/EdgeGestureArrowManager/EdgeGestureArrowManager.jar
find out/ -name "EdgeGestureArrowManager*" -exec rm -r {} \;
rm -r frameworks/base/libs/EdgeGestureArrowManager-tmp
修改frameworks/base/libs/EdgeGestureArrowManager/Android.mk
把jack的拷贝去掉
makefile
Android相关专栏
Android Framework专栏链接
Android专栏
作者:帅得不敢出门