安卓JNI c++调用java

### Android JNI 与 C++ 调用 Java 详解 #### 一、JNI 基础概念 JNI(Java Native Interface)即 Java 本地接口,是 Java 提供的一种允许 Java 代码与其他语言(如 C、C++)编写的代码进行交互的技术。在 Android 开发中,JNI 常用于以下场景: - 复用已有的 C/C++ 库 - 对性能要求极高的模块(如音视频处理、游戏引擎) - 保护核心代码(通过 Native 层增加逆向难度) #### 二、JNI 关键概念与原理 1. **JNI 环境与上下文** - `JNIEnv`:JNI 环境指针,包含了所有 JNI 函数的指针,是 Native 代码操作 Java 世界的桥梁 - `jobject`:Java 对象在 Native 层的表示,可指向任何 Java 对象 - `jclass`:Java 类在 Native 层的表示,通过 `FindClass` 或 `GetObjectClass` 获取 2. **数据类型映射** | Java 类型 | Native 类型 | 说明 | |---|---|---| | boolean | jboolean | 无符号 8 位 | | byte | jbyte | 有符号 8 位 | | short | jshort | 有符号 16 位 | | int | jint | 有符号 32 位 | | long | jlong | 有符号 64 位 | | float | jfloat | 32 位 | | double | jdouble | 64 位 | | char | jchar | 无符号 16 位 | | String | jstring | Java 字符串映射 | | Object | jobject | 所有对象基类 | #### 三、C++ 调用 Java 的实现步骤 ### 1. 准备 Java 代码 首先在 Java 中声明 Native 方法,并加载动态库: ```java public class JNIDemo { // 加载 Native 库 static { System.loadLibrary("native-lib"); } // 声明 Native 方法(由 C++ 实现) public native String getFromC(); // Java 中供 C++ 调用的方法 public String provideDataToC() { return "Hello from Java!"; } // 静态方法示例 public static int staticMethodInJava() { return 100; } } ``` ### 2. 生成头文件(JNI 接口) 通过命令行生成头文件(需确保 JDK 已配置到环境变量): ```bash # 假设 Java 类路径为 com/example/jnidemo/JNIDemo.java javac com/example/jnidemo/JNIDemo.java javah -jni com.example.jnidemo.JNIDemo ``` 生成的头文件(如 `com_example_jnidemo_JNIDemo.h`)内容示例: ```c /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_example_jnidemo_JNIDemo */ #ifndef _Included_com_example_jnidemo_JNIDemo #define _Included_com_example_jnidemo_JNIDemo #ifdef __cplusplus extern "C" { #endif /* * Class: com_example_jnidemo_JNIDemo * Method: getFromC * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_example_jnidemo_JNIDemo_getFromC (JNIEnv *, jobject); #ifdef __cplusplus } #endif #endif ``` ### 3. 实现 C++ 代码 在 C++ 中实现 Native 方法,并调用 Java 方法: ```cpp #include <jni.h> #include <string> #include <android/log.h> // 日志标签 #define TAG "JNIDemo" #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) extern "C" JNIEXPORT jstring JNICALL Java_com_example_jnidemo_JNIDemo_getFromC(JNIEnv *env, jobject thiz) { LOGD("C++ 方法被调用"); // ====== 调用 Java 实例方法 ====== // 1. 获取 Java 类 jclass jniDemoClass = env->GetObjectClass(thiz); if (!jniDemoClass) { LOGD("获取类失败"); return env->NewStringUTF("获取类失败"); } // 2. 获取方法 ID(参数:类、方法名、方法签名) jmethodID methodId = env->GetMethodID(jniDemoClass, "provideDataToC", "()Ljava/lang/String;"); if (!methodId) { LOGD("获取方法 ID 失败"); return env->NewStringUTF("获取方法失败"); } // 3. 调用方法(参数:对象、方法 ID) jstring resultFromJava = (jstring)env->CallObjectMethod(thiz, methodId); const char* resultChars = env->GetStringUTFChars(resultFromJava, NULL); LOGD("从 Java 获取的字符串: %s", resultChars); env->ReleaseStringUTFChars(resultFromJava, resultChars); // ====== 调用 Java 静态方法 ====== jclass staticJniDemoClass = env->FindClass("com/example/jnidemo/JNIDemo"); jmethodID staticMethodId = env->GetStaticMethodID(staticJniDemoClass, "staticMethodInJava", "()I"); jint staticResult = env->CallStaticIntMethod(staticJniDemoClass, staticMethodId); LOGD("静态方法返回值: %d", staticResult); // ====== 创建 Java 对象 ====== // 1. 获取构造函数 ID jclass stringClass = env->FindClass("java/lang/String"); jmethodID stringConstructor = env->GetMethodID(stringClass, "<init>", "([BL)V"); // 2. 创建字符数组 jbyteArray byteArray = env->NewByteArray(13); jbyte bytes[] = {72, 101, 108, 108, 111, 32, 67, 43, 43, 33, 0, 0, 0}; env->SetByteArrayRegion(byteArray, 0, 13, bytes); // 3. 创建 String 对象 jobject newString = env->NewObject(stringClass, stringConstructor, byteArray, 13); env->DeleteLocalRef(byteArray); // 返回 C++ 构造的字符串 return (jstring)newString; } ``` ### 4. 配置 Android.mk 编译动态库 在 NDK 开发中,通过 Android.mk 编译 C++ 代码: ```makefile LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) # 模块名称,对应 System.loadLibrary("native-lib") LOCAL_MODULE := native-lib # C++ 源文件 LOCAL_SRC_FILES := native-lib.cpp # 链接的库 LOCAL_LDLIBS += -llog include $(BUILD_SHARED_LIBRARY) ``` ### 5. 在 Java 中使用 ```java public class MainActivity extends AppCompatActivity { private JNIDemo jniDemo; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); jniDemo = new JNIDemo(); String result = jniDemo.getFromC(); Log.d("MainActivity", "从 C++ 获取的结果: " + result); } } ``` #### 四、C++ 调用 Java 的核心技术点 ### 1. 获取 Java 类的方式 - **通过对象获取类**:`jclass clazz = env->GetObjectClass(jobject);` - **直接查找类**:`jclass clazz = env->FindClass("com/example/Class");`(注意使用全限定名,且包名用'/'代替'.') ### 2. 调用 Java 方法的步骤 1. 获取方法 ID:`jmethodID methodId = env->GetMethodID(clazz, "methodName", "signature");` 2. 调用方法: - 实例方法:`env->CallObjectMethod(obj, methodId, ...);` - 静态方法:`env->CallStaticObjectMethod(clazz, methodId, ...);` - 其他类型方法对应不同的 CallXXXMethod 函数 ### 3. 方法签名规则 方法签名用于标识方法的参数和返回值类型,格式为:`(参数类型列表)返回值类型` - 基本类型签名:`I`(int)、`Z`(boolean)、`D`(double)等 - 对象类型签名:`Ljava/lang/String;`(String)、`[I`(int[])等 ### 4. 内存管理注意事项 - 本地引用(Local Reference):通过 NewObject、FindClass 等创建,需手动释放 `env->DeleteLocalRef(localRef);` - 全局引用(Global Reference):用于长期保存 Java 对象 `jobject globalRef = env->NewGlobalRef(localRef);` - 弱全局引用(Weak Global Reference):避免强引用导致内存泄漏 `jobject weakRef = env->NewWeakGlobalRef(localRef);` #### 五、高级应用场景 ### 1. 回调机制(Java 方法作为 C++ 回调函数) ```cpp // 在 C++ 中保存 Java 回调方法 jobject gCallbackObj = NULL; jmethodID gCallbackMethod = NULL; // 初始化回调 void setCallback(JNIEnv* env, jobject obj) { gCallbackObj = env->NewGlobalRef(obj); jclass clazz = env->GetObjectClass(obj); gCallbackMethod = env->GetMethodID(clazz, "onCallback", "(Ljava/lang/String;)V"); } // 在 C++ 中触发回调 void triggerCallback(JNIEnv* env, const char* message) { if (gCallbackObj && gCallbackMethod) { jstring jMsg = env->NewStringUTF(message); env->CallVoidMethod(gCallbackObj, gCallbackMethod, jMsg); env->DeleteLocalRef(jMsg); } } ``` ### 2. 操作 Java 数组 ```cpp // 创建 Java 数组 jintArray createIntArray(JNIEnv* env, int* data, int length) { jintArray array = env->NewIntArray(length); env->SetIntArrayRegion(array, 0, length, data); return array; } // 读取 Java 数组 void readIntArray(JNIEnv* env, jintArray array) { jint* data = env->GetIntArrayElements(array, NULL); int length = env->GetArrayLength(array); // 处理数据... env->ReleaseIntArrayElements(array, data, 0); // 0 表示不释放但同步修改 } ``` #### 六、常见问题与解决方案 1. **ClassNotFoundException** - 原因:FindClass 时类名错误或类未加载 - 解决:确保使用全限定名,且包名用'/'替换'.' 2. **NoSuchMethodError** - 原因:方法名或签名错误 - 解决:使用 `javap -s ClassName` 查看正确签名 3. **内存泄漏** - 原因:未正确释放本地引用 - 解决:及时调用 `DeleteLocalRef`,对长期使用的对象使用全局引用 4. **线程问题** - 注意:JNIEnv 不是线程安全的,每个线程需要独立的 JNIEnv - 解决:使用 `AttachCurrentThread` 关联线程获取 JNIEnv 通过以上步骤和示例,你可以在 Android 开发中实现 C++ 对 Java 的调用。JNI 是连接 Java 与 Native 层的重要桥梁,掌握其使用方法对于开发高性能、复杂的 Android 应用至关重要。
posted on 2025-06-07 13:44  小沙盒工作室  阅读(59)  评论(0)    收藏  举报