前言
在开发过程中,java调用jni的native方法时除了使用基本数据类型作为参数传递,还会使用数组和引用类型参数。本节就会讲解jni层与java层传递数组类型和引用类型参数。
一、JNI处理java传递过来的数组类型
举个例子,假如在java代码中有一个int型的数组,我们要在jni层里对这个数组按照从小到大的顺序排序。
JniMain.java中的代码
public class JniMain {
//----------------------jni访问数组-----------------------
public native void giveArray(int[] arr);
//---------------------------------------------
static{
System.loadLibrary("JNI_Demo1");
}
public static void main(String[] args) {
JniMain jm = new JniMain();
//给jni传递数组
int[] arr = {3,40,6,17,5};
jm.giveArray(arr);
for(int i=0;i<5;i++) {
System.out.println(arr[i]);
}
}
上面代码定义了一个整形数组,然后调用jni层的native方法进行排序,然后打印输出。
jni_impl.c中的代码
#include "stdafx.h"
#include "JniMain.h"
#include <string.h>
#include <Windows.h>
#include <stdlib.h>
//声明函数
int compare(int * a, int * b);
//访问基本数据类型
JNIEXPORT void JNICALL Java_JniMain_giveArray
(JNIEnv * env, jobject jobj, jintArray arr){
boolean iscp;
//jintArray -> jint *
jint * elements = (*env)->GetIntArrayElements(env,arr,&iscp);
if (elements == NULL)
{
return;
}
//数组长度
int len = (*env)->GetArrayLength(env,arr)
qsort(elements, len, sizeof(jint), compare);
if (iscp == JNI_TRUE){
//释放内存,并将JNI修改的数据重新写回到原来的内存
(*env)->ReleaseIntArrayElements(env, arr, elements, JNI_COMMIT);
}
}
int compare(int * a,int * b){
return *a - *b;
}
上面的代码是将java层的整形数组在JNI对应的jintArray类型arr转换成的指向jint类型的指针数组,然后对这个指针数组elements使用快速排序方法进行从小到大排序,再把elements重新赋值给arr。
打印结果如下
二、JNI层与java层引用类型的传递
代码示例有以下内容
1.jni层向java层传递一个String类型的数组。
2.jni层创建一个局部引用,是用完之后立即删除。
3.jni层创建一个全局引用,是用完之后立即删除。
4.jni层创建一个弱全局引用,在gc时就会自动回收。
Jni_Main.java
public class JniMain {
static{
System.loadLibrary("JNI_Demo1");
}
//---------------------访问引用类型-------------------
public native String[] initStringArray(int size);
//局部引用
public native void localRef();
public native void createGlobalRef();
public native String getGlobalRef();
public native void delGlobalRef();
public native String createWeakRef();
//------------------------------------------------
public static void main(String[] args) {
//调用非静态native方法
JniMain jm = new JniMain();
jm.localRef();
jm.createGlobalRef();
System.out.println(jm.getGlobalRef());
jm.delGlobalRef();
}
}
jni_impl.c
#include "stdafx.h"
#include "JniMain.h"
#include <string.h>
#include <Windows.h>
#include <stdlib.h>
/*
* Class: JniMain
* Method: initStringArray
* Signature: (I)[Ljava/lang/String;
*/
JNIEXPORT jobjectArray JNICALL Java_JniMain_initStringArray
(JNIEnv * env, jobject jobj, jint size){
//创建jobjectArray
jobjectArray result;
jclass 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 (int i = 0; i < size;i++){
//c 字符串
char * c_str = (char *)malloc(sizeof(char)* 256);
memset(c_str, 0, sizeof(char)* 256);
//将int 转换成为char
sprintf(c_str, "hello num:%d\n", i);
//char数组 -> 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);
}
return result;
}
//JNI 引用
//局部引用
JNIEXPORT void JNICALL Java_JniMain_localRef
(JNIEnv *env, jobject jobj){
int i = 0;
for ( i = 0; i < 5; i++)
{
jclass cls = (*env)->FindClass(env, "java/util/Date");
jmethodID mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
//创建一个局部Date类型的引用
jobject obj = (*env)->NewObject(env,cls,mid);
//使用这个引用
//释放引用
(*env)->DeleteLocalRef(env,cls);
(*env)->DeleteLocalRef(env, obj);
}
}
//全局引用
//NewGlobalRef 是创建全局引用的唯一方法
jstring global_str;
JNIEXPORT void JNICALL Java_JniMain_createGlobalRef
(JNIEnv * env, jobject jobj){
jobject obj = (*env)->NewStringUTF(env, "JNI is interstring");
global_str = (*env)->NewGlobalRef(env,obj);
}
JNIEXPORT jstring JNICALL Java_JniMain_getGlobalRef
(JNIEnv * env, jobject jobj){
return global_str;
}
JNIEXPORT void JNICALL Java_JniMain_delGlobalRef
(JNIEnv * env, jobject jobj){
(*env)->DeleteGlobalRef(env, global_str);
}
//弱全局引用
//它不会阻止gc
jclass g_weak_cls;
JNIEXPORT jstring JNICALL Java_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中的异常处理
Jni_Main.java
public class JniMain {
static{
System.loadLibrary("JNI_Demo1");
}
public native void exception();
public static void main(String[] args) {
JniMain jm = new JniMain();
//异常处理
try {
jm.exception();
}catch(Exception e) {
System.out.println(e.toString());
}
}
}
jni_impl.c
#include "stdafx.h"
#include "JniMain.h"
#include <string.h>
#include <Windows.h>
#include <stdlib.h>
//JNI异常处理
JNIEXPORT void JNICALL Java_JniMain_exception
(JNIEnv * env, jobject jobj){
jclass cls = (*env)->GetObjectClass(env, jobj);
jfieldID cls = (*env)->GetFieldID(env, cls, "mykey", "Ljava/lang/String;");
//检查是否发送异常
jthrowable ex = (*env)->ExceptionOccurred(env);
if (ex != NULL)
{
//清空之前产生的异常
(*env)->ExceptionClear(env);
jclass newExc;
newExc = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
if (newExc == NULL)
{
printf("exception\n");
return;
}
(*env)->ThrowNew(env, newExc, "Throw exception from JNI: GetFieldID faild");
}
}