JNI编程五:JNI处理java中的数组类型、引用类型、异常处理


前言

在开发过程中,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");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值