静态注册
通过函数名建立JNI和java层的一一对应关系。
实现过程
- 写java代码
- 用javah指令产生对应的 *.h 文件
- 在 *.h 中实现定义
缺点
- 不方便编写, JNI方法名需要遵守注册规则,且名称非常的长。
- 编写过程中太多步骤 ,很不方便。
- 程序运行不够高效,因为初始调用native 函数要去搜索JNI 层对应函数名的本地函数,然后建立对应关系,这个过程消耗一定的时间。
命名规则
Java开头,然后是下划线,紧接着是下划线分割的package name,然后跟着的是下划线和class name,最后是下划线和方法名。
JNIEXPORT jstring JNICALL
Java_com_example_efan_jni_1learn2_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
动态注册
使用RegisterNatives 方法来注册jni函数与java方法为一一对应关系。
实现过程
- 使用JNINativeMethod 数组来记录对应的java和jni方法;
- 实现JNI_OnLoad 方法,在载入动态库时进行动态注册;
- 使用FindClass方法来获取java对象;
- 调用RegisterNatives方法,传入java对象和JNINativeMethod 数组,和注册去完成的数目;
优点
- 过程更加清晰和可控;
- 高效
jstring stringFromJNI(JNIEnv *env, jobject thiz){
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
static const JNINativeMethod gMethods[] = {
{"stringFromJNI", "()Ljava/lang/String;", (jstring*)stringFromJNI}
};
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){
__android_log_print(ANDROID_LOG_INFO, "native", "Jni_OnLoad");
JNIEnv* env = NULL;
If(vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) //Get JNIEnv from JavaVM, generally use version 1.4
return -1;
jclass clazz = env->FindClass("com/example/efan/jni_learn2/MainActivity");
if (!clazz){
__android_log_print(ANDROID_LOG_INFO, "native", "cannot get class: com/example/efan/jni_learn2/MainActivity");
return -1;
}
if(env->RegisterNatives(clazz, gMethods, sizeof(gMethods)/sizeof(gMethods[0])))
{
__android_log_print(ANDROID_LOG_INFO, "native", "register native method failed!\n");
return -1;
}
return JNI_VERSION_1_4;
}
JNINativeMethod
该结构体用于动态注册过程 , JNINativeMethod 被用来记录对应的jni方法和java方法。
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
第一个参数,name ,java方法的名称。
第二个参数,signature,用来描述方法的参数和返回值。
第三个参数,fnPtr , 是一个函数指针指向jni函数。
其中,第二参数signature用字符串类型为参数记录方法和返回值类型。具体的格式像”()V” , “(II)V” ,格式分为两个部分,括号内部和括号右边的参数,右边的参数表示返回值。
数据类型映射
基础数据类型
Java type | Native type | Domain descriptor | supplement |
---|---|---|---|
boolean | jboolean | Z | |
byte | jbyte | B | |
char | jchar | C | |
short | jshort | S | |
int | jint | I | |
long | jlong | J | |
float | jfloat | F | |
double | jdouble | D | |
void | void | V |
对象引用类型
对于其他引用类型,例如java对象,映射规则如下:
Java type | Native type | Domain descriptor | supplement |
Class name (such as Surface) | Usually jobject, with one exception, if the java type is a String, the corresponding native type is jstring | Start with “L” and end with “;” in the middle of the package and class name separated by “/” (such as Landroid/view/Surface;) if the inner class uses $ to connect the inner class; |
对象数组引用规则
如果他是一个一维数组,如下表。如果是二维或者更高维数组,对应的native类型是jobjectArray , 并且'[‘ 的数目表示维度。
Java type | Native type | Domain descriptor | supplement |
---|---|---|---|
Class name (such as Surface) | Usually jobject, with one exception, if the java type is a String, the corresponding native type is jstring | Add ‘[‘ character to the left based on the domain descriptor of the object reference type |
jni 函数的默认参数
有两个默认参数在jni函数
JNIEnv *env, jobject thiz
JNIEnv 指向当前java环境,可以使用JNIEnv来操纵java层代码;jobject 指向对应的java native方法对应的jni函数的类实例,如果java方法是静态的,它就代表类对象的引用;
https://github.com/doom-man/jnidemo
0 条评论