JNI
Java Native Interface 缩写为 JNI,通过 JNI 可以:
- java 程序可以调用 native 函数;
- native 程序可以反向调用 java 函数
注意:JNI 层必须实现为动态库的形式,这样 JVM 才能加载并调用 so 中的函数
总结:JNI 是连通 java 层和 native 层的桥梁
加载动态库与 native 函数声明
1
2
3
4
5
6
7
8
| static {
// 加载对应的 JNI 库,name 是库的名称,实际加载时:
// linux -> libname_jni.so, windows -> name.dll
System.loadLibrary("name");
}
// java 中使用 native 关键字声明 native 函数,具体实现在 JNI 层
private static native void native_init();
|
JNI 函数注册
静态注册
- 先编写 java 代码,然后编译生成 .class 文件;
- 使用 jdk 提供的工具
javah
,如 javah -o putput package.classname
生成头文件 output.h
,该头文件中声明了在 java 层声明的 JNI 接口,native 层实现该函数即可 - 实例:
- 第一步: java 层声明 native 函数
1
2
3
4
| package com.example.android;
public class HelloNdk {
public native void sayHello();
}
|
- 第二步:生成的头文件
1
2
| // 格式:Java_包名_类名_函数名,其中包名中的 . 要替换为 _
JNIEXPORT void JNICALL Java_com_example_android_HelloNdk_sayHello();
|
- native 函数如何找到对应的 JNI 函数 当 java 层调用 native 函数时,会从对应的 JNI 库中搜索
Java_com_android_example_HelloNdk_sayHello
函数,如找不到则抛错;如找到则将 sayHello
与该函数建立关联,即保存 JNI 层的函 数指针;之后再调用该函数时就可以直接使用该函数指针,这些工作均是由虚拟机完成。 - 静态注册的缺点
- 需要对每一个声明了 native 方法的 java 类进行编译并且使用 javah 生成头文件;
- javah 生成的 JNI 层函数名过长,不易书写;
- 初次运行需要更具
动态注册
Java 和 JNI 层数据类型的转换
JNIEnv 和 jstring 的使用方法及 JNI 中的类型签名
垃圾回收在 JNI 层中的使用及异常处理
char* to jcharArray
1
2
3
4
5
6
7
8
9
10
11
12
13
| static jcharArray getData_native(JNIEnv *env, jobject clazz) {
struct nrf_ctrl tx_ctrl;
tx_ctrl.tx_ctrl = {'D', 'A', 'T', 'A'};
int len = 4;
jcharArray data = env->NewCharArray(len);
jchar *temp = (jchar *) calloc(len, sizeof(jchar));
for(int i = 0; i < len; i+=2) {
*(temp + i) = *(rx_ctrl.rxbuf + i);
*(temp + i + 1) = *(rx_ctrl.rxbuf + i + 1);
}
env->SetCharArrayRegion(data, 0, len, temp);
return data;
}
|
jstring to char*
1
2
3
4
| static void sendData_native(JNIEnv *env, jobject clazz, jstring data) {
const char* _data = env->GetStringUTFChars(data, 0);
LOGI("send cmd to Nrf channels pipe: %s\n", _data);
}
|
jcharArray to char*
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| static void sendCommand_native(JNIEnv *env, jobject clazz, jcharArray cmd) {
int fd = open("/dev/nrf_ctrl", O_RDWR);
if (fd <= 0) {
LOGE("send cmd to Nrf failed with fd: %d", fd);
return;
}
struct nrf_ctrl tx_ctrl;
tx_ctrl.length = 6;
tx_ctrl.txbuf = new char[tx_ctrl.length];
jchar* _cmd = env->GetCharArrayElements(cmd, nullptr);
for (int i = 0; i < tx_ctrl.length; i++) {
tx_ctrl.txbuf[i] = *(_cmd + i);
LOGI("send cmd to Nrf %d: j: %02x, c: %02x", i, *(_cmd + i), tx_ctrl.txbuf[i]);
}
int ret = ioctl(fd, NRF_IOCTL_SET_DEVICE_GMDCMD, &tx_ctrl);
if (ret < 0) {
LOGE("send cmd to Nrf failed with ret: %d", ret);
}
// release
env->ReleaseCharArrayElements(_cmd, cmd, JNI_ABORT);
close(fd);
}
|