一 JNI访问数组
- 定义native方法生产.h文件
public class JniMain {
public native void giveArray(int[] inArray);
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_ray_JniMain */
#ifndef _Included_com_ray_JniMain
#define _Included_com_ray_JniMain
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_ray_JniMain
* Method: giveArray
* Signature: ([I)V
*/
JNIEXPORT void JNICALL Java_com_ray_JniMain_giveArray
(JNIEnv *, jobject, jintArray);
#ifdef __cplusplus
}
#endif
#endif
- c中实现h定义的方法,生成dell文件
#include "stdafx.h"
#include <stdio.h>
#include "com_ray_JniMain.h"
#include <string.h>
#include <Windows.h>
//访问基本类型数据数组
JNIEXPORT void JNICALL Java_com_ray_JniMain_giveArray
(JNIEnv * env, jobject jobj, jintArray arr) {
int compare(jint * a, jint *b);
//jintArray->jint *
jint *elements = (*env)->GetIntArrayElements(env, arr, NULL);
if (elements == NULL) {
return;
}
//数组长度
int len = (*env)->GetArrayLength(env, arr);
qsort(elements, len, sizeof(jint), compare);
//释放可能的内存
//将JNI 修改的数据重新写回原来的内存
(*env)->ReleaseIntArrayElements(env, arr, elements, JNI_COMMIT);
}
int compare(jint * a, jint *b) {
return *a - *b;
}
- 集成dll,调用native方法
public class JniMain {
public native void giveArray(int[] inArray);
static {
System.loadLibrary("lsn8");
}
public static void main(String[] args) {
JniMain jniMain = new JniMain();
int[] array = {3,9,2,50,6,13};
jniMain.giveArray(array);
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
}
运行结果:
2
3
6
9
13
50
二 访问引用数据类型的数组
- native方法
public native String[] initStringArray(int size);
- 实现.h声明方法
// 访问引用数据类型的数组
JNIEXPORT jobjectArray JNICALL Java_com_ray_JniMain_initStringArray
(JNIEnv * env, jobject jobj, jint size) {
//创建jobjectArray
jobjectArray result;
jclass jclz;
int i;
jclz = (*env)->FindClass(env, "java/lang/String");
if (jclz == NULL) {
return NULL;
}
result = (*env)->NewObjectArray(env, size, jclz, jobj);
if (result == NULL) {
return NULL;
}
// 赋值
for (i = 0; i < size; i++) {
//C 字符串
char * c_str = (char *)malloc(256);
memset(c_str, 0, 256);
//将int 转换成为char
sprintf(c_str, "hello num: %d\n", i);
//C ->jstring
jstring str = (*env)->NewStringUTF(env, c_str);
if (str == NULL) {
return NULL;
}
//将jstring 赋值给数组
(*env)->SetObjectArrayElement(env, result, i, str);
free(c_str);
c_str = NULL;
//(*env)->DeleteGlobalRef(env, str);
}
//返回jobjectArray
return result;
}
- 调用
String[] strArr = jniMain.initStringArray(5);
for (int i = 0; i < strArr.length; i++) {
System.out.format("strArr[%d] = %s\n",i, strArr[i]);
}
- 运行结果
strArr[0] = hello num: 0
strArr[1] = hello num: 1
strArr[2] = hello num: 2
strArr[3] = hello num: 3
strArr[4] = hello num: 4
三 JNI引用
1 局部引用
定义方式多样: FindClass,NewObject,GetObjectClass,NewCharArray… NewLocalRef()
释放方式: 1 方法调用完JVM 会自动释放 2.DeleteLocalRef
注意: 不能在多线程里面使用
JNIEXPORT void JNICALL Java_com_ray_JniMain_localRef
(JNIEnv * env, jobject jobj) {
int i = 0;
for (i = 0;i < 5;i++) {
jclass cls = (*env)->FindClass(env, "java/util/Date");
jmethodID jmid = (*env)->GetMethodID(env, cls, "<init>", "()V");
//创建一个Date类型的局部引用
jobject obj = (*env)->NewObject(env, cls, jmid);
//使用这个引用
//释放引用
(*env)->DeleteLocalRef(env, cls);
(*env)->DeleteLocalRef(env, obj);
}
}
2 全局引用
//跨线程,跨方法使用
// NewGlobalRef 是创建全局引用的唯一方法
public native void createGlobalRef();
public native String getglobalRef();
public native void delGlobalRef();
jstring global_str;
JNIEXPORT void JNICALL Java_com_ray_JniMain_createGlobalRef
(JNIEnv * env, jobject jobj) {
jobject obj = (*env)->NewStringUTF(env, "JNI is intersting");
global_str = (*env)->NewGlobalRef(env, obj);
}
JNIEXPORT jstring JNICALL Java_com_ray_JniMain_getglobalRef
(JNIEnv * env, jobject jobj) {
return global_str;
}
JNIEXPORT void JNICALL Java_com_ray_JniMain_delGlobalRef
(JNIEnv * env, jobject jobj) {
(*env)->DeleteGlobalRef(env, global_str);
}
调用:
jniMain.createGlobalRef();
System.out.println(jniMain.getglobalRef());
jniMain.delGlobalRef();
System.out.println("globalRef is released");
3 弱全局引用
//它不会阻止GC,/跨线程,跨方法使用
jclass g_weak_cls;
JNIEXPORT jstring JNICALL Java_com_ray_JniMain_createWeakRef
(JNIEnv * env, jobject jobj) {
jclass cls_string = (*env)->FindClass(env, "java/lang/String");
g_weak_cls = (*env)->NewWeakGlobalRef(env, cls_string);
return g_weak_cls;
}
四 异常
- jni里面程序出现异常,java调用的时候程序中断,try catch无法捕获
- jni 通过 throwable ex = (*env)->ExceptionOccurred(env)获取异常,(*env)->ExceptionClear(env);清除异常
- 创建异常: newExc = (*env)->FindClass(env, “java/lang/IllegalArgumentException”)
将异常传递到java层: (*env)->ThrowNew(env, newExc, "Throw exception from JNI: GetFieldID faild ")
public class JniMain {
//public String key= "key";
}
public native void exception();
static {
System.loadLibrary("lsn8");
}
public static void main(String[] args) {
JniMain jniMain = new JniMain();
//将key注释掉,找不到key报异常;取消注释运行异常
try {
jniMain.exception();
} catch (Exception e) {
System.out.println(e.toString());
}
}
c中实现native方法:
//JNI 异常处理
JNIEXPORT void JNICALL Java_com_ray_JniMain_exception
(JNIEnv * env, jobject jobj) {
jclass cls = (*env)->GetObjectClass(env, jobj);
jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
//检查是否发送异常
jthrowable ex = (*env)->ExceptionOccurred(env);
// 判断异常是否发送
if (ex != NULL) {
jclass newExc;
//清空JNI 产生的异常
(*env)->ExceptionClear(env);
//IllegalArgumentException
newExc = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
if (newExc == NULL)
{
printf("exception\n");
return;
}
(*env)->ThrowNew(env, newExc, "Throw exception from JNI: GetFieldID faild ");
}
}
运行结果:
java.lang.IllegalArgumentException: Throw exception from JNI: GetFieldID faild
五 缓存
static jclass cls_string = NULL;
- 方法内声明的只在方法中有效
- 方法外的,在声明之后,本文件内有效
1 局部静态变量进行缓存
//
JNIEXPORT void JNICALL Java_com_ray_JniMain_cached
(JNIEnv * env, jobject jobj) {
jclass cls = (*env)->GetObjectClass(env, jobj);
static jfieldID fid = NULL;
if (fid == NULL) {
fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
printf("GetFieldID\n");
}
}
重复调用:
for(int i = 0;i <100;i++) {
jniMain.cached();
}
只输出一次:
GetFieldID
如果声明成:jfieldID fid = NULL,不加static,会打印100次;
2 全局变量
static jfieldID fid_glb;
JNIEXPORT void JNICALL Java_com_ray_JniMain_cachedGlobal
(JNIEnv * env, jclass cls) {
if (fid_glb == NULL) {
fid_glb = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
printf("cachedGlobal GetFieldID\n");
}
}
public native static void cachedGlobal();
public static void main(String[] args) {
for(int i = 0;i <100;i++) {
cachedGlobal();
}
}
如果去掉static,则整个文件都可以使用
六 缓存策略和弱引用联合使用带来的问题
1 定义native方法
public native String AcessCacheNewString();
public native String AcessCF();
2 实现native方法,利用static缓存
JNIEXPORT jstring JNICALL Java_com_ray_JniMain_AcessCacheNewString
(JNIEnv * env, jobject jobj) {
static jclass cls_string = NULL;
if (cls_string == NULL) {
printf("Java_JniMain_AcessCache_newString out: \n");
cls_string = (*env)->FindClass(env, "com/ray/Refrence");
}
//使用这个静态局部变量
jmethodID jmid = (*env)->GetMethodID(env, cls_string, "getRef", "(I)I");
jthrowable ex = (*env)->ExceptionOccurred(env);
if (ex != NULL)
{
jclass newExc;
// 让java 继续运行
(*env)->ExceptionDescribe(env);//输出关于这个异常的描述
(*env)->ExceptionClear(env);
printf("C exceptions happend\n");
}
printf("ray out java_jnimain_acesscache_newstring\n");
return NULL;
}
JNIEXPORT jstring JNICALL Java_com_ray_JniMain_AcessCF
(JNIEnv * env, jobject jobj) {
return Java_com_ray_JniMain_AcessCacheNewString(env, jobj);
}
3 生产dll,只调用一次,正常
public static void main(String[] args) {
JniMain jniMain = new JniMain();
jniMain.AcessCF();
}
输出结果:
Java_JniMain_AcessCache_newString out:
ray out java_jnimain_acesscache_newstring
4 申请大内存,调用多次
public static void main(String[] args) {
jniMain.AcessCF();
for(int i = 0; i < 100; i++) {
jniMain.AcessCF();
//大数组申请大量内存触发GC
long[] ar = new long[1024*1024];
}
}
输出报错:
java.lang.NoSuchMethodError: getRef
Java_JniMain_AcessCache_newString out:
ray out java_jnimain_acesscache_newstring
ray out java_jnimain_acesscache_newstring
ray out java_jnimain_acesscache_newstring
ray out java_jnimain_acesscache_newstring
ray out java_jnimain_acesscache_newstring
C exceptions happend
ray out java_jnimain_acesscache_newstring
Exception in thread “main”
5 解决
- 一般请求不要将局部引用赋值给静态的局部变量
- 将static jclass cls_string的static去掉
- 如果要用静态的局部变量,在使用之前判断它的有效性,抓获异常,保证程序执行