NDK中动态注册JNI方法。
Java中定义了native方法后,在C/C++中使用JNI_OnLoad
函数来注册相应的方法。
一般做法如下:
- Java中定义native方法,确定Java文件的包名和路径
- 编写C/C++文件
- 实现相应的jni方法
- 根据Java文件路径和方法签名确定
JNINativeMethod
- 实现
JNIEXPORT int JNICALL JNI_OnLoad(JavaVM *vm,void *reserved)
方法- 注册jni方法
RegisterNatives
- 注册jni方法
- 编译生成so文件
- Java中加载so库文件
- Java中调用native方法
相比于之前的静态注册,这里没有用javah
生成头文件。
关于查询Java方法签名
关于查询Java方法签名,可以使用javap -s
命令查询相应的class文件。
例如查看DynamicJNI.java
的方法签名,先编译出DynamicJNI.class
文件,再查询。
$ javap -s ./build/intermediates/classes/debug/com/rustfisher/appndkground/jni/DynamicJNI.class
Compiled from "DynamicJNI.java"
public class com.rustfisher.appndkground.jni.DynamicJNI {
public com.rustfisher.appndkground.jni.DynamicJNI();
descriptor: ()V
public static native java.lang.String getHello();
descriptor: ()Ljava/lang/String;
public static native int meaningOfTheUniverse();
descriptor: ()I
static {};
descriptor: ()V
}
查询得知getHello()
的方法签名为()Ljava/lang/String
;
meaningOfTheUniverse()
方法签名为()I
;
如果方法签名错了,编译能通过,但运行时会报NoSuchMethod异常
动态注册JNI方法示例
主要文件:
-
dynamic_jni.cpp
方法实现文件 -
DynamicJNI.java
jni接口
DynamicJNI.java
中定义native方法。ndkground
是模块名称。
public class DynamicJNI {
static {
System.loadLibrary("ndkground");
}
public static native String getHello();
public static native int meaningOfTheUniverse();
}
文件路径是"com/rustfisher/appndkground/jni/DynamicJNI"
,这个路径确定后一般不再改动。
dynamic_jni.cpp
实现相关的方法
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <jni.h>
#define JNI_REG_CLASS "com/rustfisher/appndkground/jni/DynamicJNI" // path of Java file
JNIEXPORT jstring JNICALL get_hello(JNIEnv *env, jclass clazz) {
return env->NewStringUTF("hello from jni");
}
JNIEXPORT jint JNICALL meaning_of_the_universe(JNIEnv *env, jclass clazz) {
return 42;
}
static JNINativeMethod g_methods[] = {
{ "getHello", "()Ljava/lang/String;", (void*)get_hello},
{ "meaningOfTheUniverse", "()I", (void*)meaning_of_the_universe},
};
// must define this function
JNIEXPORT int JNICALL JNI_OnLoad(JavaVM *vm,void *reserved) {
JNIEnv *env;
if (vm->GetEnv((void **) &env,JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}
jclass javaClass = env->FindClass(JNI_REG_CLASS);
if (javaClass == NULL){
return JNI_ERR;
}
int method_count = sizeof(g_methods) / sizeof(g_methods[0]);
if (env->RegisterNatives(javaClass, g_methods, method_count) < 0) {
return JNI_ERR;
}
return JNI_VERSION_1_6;
}
这里使用Cygwin执行ndk-build
来编译生成so文件。
调用动态注册的jni方法 DynamicJNI.getHello()
相应代码参见 https://github.com/RustFisher/android-Basic4/tree/master/appNdkGround