JNI的两种注册方式比较

基本数据类型对照表

Java类型 C语言类型 JNI对应的类型
int long jint
long _int64 jlong
byte signed char jbyte
boolean unsigned char jboolean
char unsigned short jchar
short short jshort
float float jfloat
double double jdouble

静态注册

这是很多例子用的最多,也是最为常见的一种方式。

  • 原理

    根据声明的java本地方法生成的头文件规则,通过函数名的对应关系来达到

java声明

1
2
3
4
5
6
7
8
9
10
11
12
package io.jewsaten.opencvsamples;
/**
* Created by Administrator on 2017/9/19.
*/
public class OpenCVUtil {
static {
System.loadLibrary("OpenCV");
}
public static native int[] gray(int[] pixel, int w, int h);
}

jni生成的头文件声明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class io_jewsaten_opencvsamples_OpenCVUtil */
#ifndef _Included_io_jewsaten_opencvsamples_OpenCVUtil
#define _Included_io_jewsaten_opencvsamples_OpenCVUtil
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: io_jewsaten_opencvsamples_OpenCVUtil
* Method: gray
* Signature: ([III)[I
*/
JNIEXPORT jintArray JNICALL Java_io_jewsaten_opencvsamples_OpenCVUtil_gray
(JNIEnv *, jclass, jintArray, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
  • 结论

(1). 如果有文件位置改动,文件名,函数名的改动的话,后期就比较麻烦

(2). 函数名长度过长,可读性差,不便维护

动态注册

前面一种方式或许只是局限在demo层面,而后一种实现方式则更多的用到实际项目中

Java调用本地函数,一般是透过VM去so文件查找相应的本地函数,如果需要连续调用多次,每次都需要查找一遍,这将是相当花时间的; 这时候开发者就可以自行借助JNI_Onload函数动态注册本地函数

  • 原理

    告诉JVM native函数在JNI中对应的函数指针,支持函数名自定义

1
2
3
4
5
6
7
8
9
10
11
//通过这个结构体建立对应关系
typedef struct {
const char* name; //Java中函数的名字
const char* signature;//签名,描述了函数的参数和返回值
void* fnPtr;//函数指针,对应的jni函数
} JNINativeMethod;

函数映射表

在这里声明各个函数间的对应关系

1
2
3
4
5
6
7
8
static JNINativeMethod gMethods[] = {
{"gray", "([III)[I", (void*)gray},
};
//这里的函数名已经不再是Java_包名_类名_方法名,可以是任意有意义的命名
JNIEXPORT jintArray JNICALL gray(JNIEnv *env, jclass clazz, jintArray buf, jint w, jint h);
  • 最后

    当你调用System.loadLibrary, VM首先执行到JNI_OnLoad,动态方式藉由此方法告知VM我们要注册的一系列本地函数,以及一些初始化操作,以便后续加快调用本地函数的效率

点我下载代码示例

打赏作者