静态注册

通过函数名建立JNI和java层的一一对应关系。

实现过程

  1. 写java代码
  2. 用javah指令产生对应的 *.h 文件
  3. 在 *.h 中实现定义

缺点

  1. 不方便编写, JNI方法名需要遵守注册规则,且名称非常的长。
  2. 编写过程中太多步骤 ,很不方便。
  3. 程序运行不够高效,因为初始调用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方法为一一对应关系。

实现过程

  1. 使用JNINativeMethod 数组来记录对应的java和jni方法;
  2. 实现JNI_OnLoad 方法,在载入动态库时进行动态注册;
  3. 使用FindClass方法来获取java对象;
  4. 调用RegisterNatives方法,传入java对象和JNINativeMethod 数组,和注册去完成的数目;

优点

  1. 过程更加清晰和可控;
  2. 高效
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 typeNative typeDomain descriptorsupplement
booleanjbooleanZ
bytejbyteB
charjcharC
shortjshortS
intjintI
longjlongJ
floatjfloatF
doublejdoubleD
voidvoidV

对象引用类型

对于其他引用类型,例如java对象,映射规则如下:

Java typeNative typeDomain descriptorsupplement
Class name (such as Surface)Usually jobject, with one exception, if the java type is a String, the corresponding native type is jstringStart 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 typeNative typeDomain descriptorsupplement
Class name (such as Surface)Usually jobject, with one exception, if the java type is a String, the corresponding native type is jstringAdd ‘[‘ 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

http://zhixinliu.com/2015/07/01/2015-07-01-jni-register/

https://www.programmersought.com/article/70121569265/

分类: 翻译

pareto

未来什么方向不管,先做自己喜欢做的事情。

0 条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注