JNI基础语法1

JNI学习笔记1

我们看看用javah生成的头文件的两种区别
package com.fmy;

public class FMY {

    //静态
    public static native String  getStringStatic();

    //非静态
    public native String getString();

}
  • 在dos窗口中进入工程src目录用javah 生成头文件
    这里写图片描述

  • 刷新工程目录 查看生成的文件 com_fmy_FMY.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_fmy_FMY */

#ifndef _Included_com_fmy_FMY
#define _Included_com_fmy_FMY
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_fmy_FMY
 * Method:    getStringStatic
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_fmy_FMY_getStringStatic
  (JNIEnv *, jclass);

/*
 * Class:     com_fmy_FMY
 * Method:    getString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_fmy_FMY_getString
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

上面代码太冗余 我们看下核心的

//静态的
JNIEXPORT jstring JNICALL Java_com_fmy_FMY_getStringStatic
  (JNIEnv *, jclass);

//非静态的
JNIEXPORT jstring JNICALL Java_com_fmy_FMY_getString
  (JNIEnv *, jobject);

可以看到 参数区别在于 第二个参数
1. 静态的参数 (JNIEnv *, jclass);
jclass是传入对象的对应的class 在本例中就是FMY.class
2. 非静态的参数 (JNIEnv *, jobject);
jobject是传入对象的对应实力 在本例中就是FMY这个实例

直接看一下C语言中两个方法的实现函数吧

下面代码大致看下就行 后面介绍

//可以不需要,按照行业规范一般需要编写头文件(文档)
#include"com_fmy_FMY.h"

JNIEXPORT jstring JNICALL Java_com_fmy_FMY_getStringStatic
(JNIEnv * env, jclass jcs){

    char *cstr = "我说中文?";
    //String类的jclass
    jclass str_cls = (*env)->FindClass(env, "java/lang/String");
    jmethodID constructor_mid = (*env)->GetMethodID(env, str_cls, "<init>", "([BLjava/lang/String;)V");

    //char * -> char[] ->jbyteArray -> jbyte * 
    jbyteArray bytes = (*env)->NewByteArray(env, strlen(cstr));
    //bytes数组赋值
    (*env)->SetByteArrayRegion(env, bytes, 0, strlen(cstr), cstr);

    //charsetName="UTF-8"
    jstring charsetName = (*env)->NewStringUTF(env, "UTF-8");

    //返回utf-8中文编码jstring
    return (*env)->NewObject(env, str_cls, constructor_mid, bytes, charsetName);
}
JNIEXPORT jstring JNICALL Java_com_fmy_FMY_getString
(JNIEnv * env, jobject jobj){
    char *cstr = "我说中文?";
    //String类的jclass
    jclass str_cls = (*env)->FindClass(env, "java/lang/String");
    jmethodID constructor_mid = (*env)->GetMethodID(env, str_cls, "<init>", "([BLjava/lang/String;)V");

    //char * -> char[] ->jbyteArray -> jbyte * 
    jbyteArray bytes = (*env)->NewByteArray(env, strlen(cstr));
    //bytes数组赋值
    (*env)->SetByteArrayRegion(env, bytes, 0, strlen(cstr), cstr);

    //charsetName="utf-8"
    jstring charsetName = (*env)->NewStringUTF(env, "UTF-8");

    //返回utf-8中文编码jstring
    return (*env)->NewObject(env, str_cls, constructor_mid, bytes, charsetName);
}

说明: 上面new 了一个String 返回 为UTF-8格式,注意这里如果你的c文件保存文件类型是GB2312那么对应//charsetName=”GB2312”
jstring charsetName = (*env)->NewStringUTF(env, “GB2312”); 这里这么繁琐是为了处理c返回string出现乱码

jni基本数据类型转换

我们知道java和中基本数据类型和c中是有一些差别的,所以jni中用宏定义来转换基本数据类型如 java中的int类型 在jni中用jnit(实际是long的宏定义)
源码:

typedef long jint;
  • 实验1 jni访问java中的int类型并且返回一个jni中定义的int类型

    java 源代码:

package com.fmy;

public class DemoJniBaseType {

    static{
        System.loadLibrary("Project1");
    }
    public static void main(String[] args) {

        int jniForInt = jniForInt(2);

        System.out.println("来自c修改后的int"+jniForInt);
    }

    //访问传入的java 的 传入参数 i 并且改变数值 再返回
    public static native int   jniForInt(int i);
}

C语言函数实现

#include"com_fmy_DemoJniBaseType.h"
JNIEXPORT jint JNICALL Java_com_fmy_DemoJniBaseType_jniForInt
(JNIEnv* env, jclass jcas, jint argument_int){

    printf("来自java的int数值为%ld",argument_int);

    //因为jint是long类型所以直接赋值
    long new_int = argument_int;

    new_int++;//加1后返回

    return new_int;


}

输出:

来自c修改后的int3
来自java的int数值为2


来看看其他基本数据类型吧

javajni本质的宏定义
booleanjbooleanunsigned char
bytejbytesigned char
shortjshortshort
floatjfloatfloat
doublejdoubledouble
longjlong__int64
voidvoid

基本数据完了我们来看看引用对象吧

//引用类型(对象)
//String jstring
//object jobject
//基本类型的数组也是引用类型
//byte[] jByteArray
//Class  jclass
javajni本质的宏定义
objectjobjectstruct _jobject *
classjclassjobject
throwablejthrowablejobject
floatjfloatjobject
stringjstringjobject
arrayjarrayjobject
byte[]jbyteArrayjarray
int[]jintArrayjarray
double[]jdoubleArrayjarray
访问java对象非静态属性

先说一下java签名 每个java属性方法都有其对应的属性签名
这里写图片描述

上面的表只有一部分签名,如果你发现你的方法没有签名在上述表格中怎么办?

打开dos窗口 打开工程木…./src/bin/目录下 然后输入javap -s -p 包名.类名

如下java源代码:

package com.fmy;


public class DemoJniBaseType {

    static
    {
        System.loadLibrary("Project1");
    }
    //创建一个非静态属性
    String helloString ="你好世界";

    public static void main(String[] args) {

        DemoJniBaseType demoJniBaseType = new DemoJniBaseType();

        System.out.println("C没修改前:-->>"+demoJniBaseType.helloString);
        //进入c去修改
        demoJniBaseType.jniCall();

        System.out.println("C没修改后:-->>"+demoJniBaseType.helloString);
    }

    //访问java静态属性并且修改为 hello world
    public  native void   jniCall();
}

这里写图片描述

然后你就可以得到方法 属性变量的签名(descriptor描述)

然后我们看看实现上面java中本地方法:

#include"com_fmy_DemoJniBaseType.h"

// 用c 去访问java中属性变量String helloString ="你好世界";并且修改为 hello world
JNIEXPORT void JNICALL Java_com_fmy_DemoJniBaseType_jniCall
(JNIEnv * env, jobject jobj){

    //获取jonj的class本例当中就是DemoJniBaseType的class对象
    jclass cls = (*env)->GetObjectClass(env,jobj);

    // 函数原型:jfieldID (JNICALL *GetFieldID)(JNIEnv *env, jclass clazz, const char *name, const char *sig);
    // 第一个参数 不需要解释 
    // 第二个参数 属性所在对象的class本例当中就是DemoJniBaseType的class对象
    // 第三个参数 属性变量名字
    // 第四个参数 属性签名
    // 返回属性变量的id 在用id得到变量引用
    jfieldID file_id = (*env)->GetFieldID(env, cls, "helloString", "Ljava/lang/String;");

    //函数原型:jobject (JNICALL *GetObjectField)(JNIEnv *env, jobject obj, jfieldID fieldID);
    //第一个参数 ..
    //第二个参数 属性id
    //返回 属性变量引用
    jstring string=(*env)->GetObjectField(env,jobj,file_id);

    //函数原型:const char* (JNICALL *GetStringUTFChars)(JNIEnv *env, jstring str, jboolean *isCopy);
    //第一个参数...
    //第二个参数 属性变量引用
    //第三个参数 建议传入NULL  特别强调:调用此方法时内部会决定是否拷贝java属性变量的指针引用
    // 如果拷贝了那么第三个参数 字面量就是true 反知
    char * cchar =(*env)->GetStringUTFChars(env, string, NULL);

    //printf("得到来自java的字符串%s",cchar);

    jstring(JNICALL *NewStringUTF)
    //  //(JNIEnv *env, const char *utf);
    jstring newjstring =    (*env)->NewStringUTF(env,"hello world");


    //函数原型:void (JNICALL *SetObjectField)(JNIEnv *env, jobject obj, jfieldID fieldID, jobject val);
    //第一个参数
    //第二个参数 属性所在对象
    //第三个参数 属性id
    //第四个参数 赋值
    //设置对象属性数值
    (*env)->SetObjectField(env, jobj, file_id,newjstring);

    //释放GetStringUTFChars 所拷贝java变量引用 
    //在取得字符串后,当已经不再之用,需要通过ReleaseStringChars、ReleaseStringUTFChars来释放拷贝所用的内存或释放Java的String对象的引用
    (*env)->ReleaseStringChars(env,string,cchar);


}
访问java对象静态属性

java

package com.fmy;

import java.io.UnsupportedEncodingException;

public class FMY {


    public static String helloString ="你好世界";


    static{
        System.loadLibrary("Project2");
    }

    public static void main(String[] args) {

        callStaticFile();

        System.out.println(helloString);
    }


    public native static void callStaticFile();


}

c语言实现

#include"com_fmy_FMY.h"

JNIEXPORT void JNICALL Java_com_fmy_FMY_callStaticFile
(JNIEnv * env, jclass jcs){

    //函数原型:jfieldID(JNICALL *GetStaticFieldID)(JNIEnv *env, jclass clazz, const char *name, const char *sig);
    //第一个参数 
    //第二个参数 静态属性对应类的class 本例中就是FMY 
    //第三个参数 静态属性名字
    //第四个参数 属性签名
    //返回 属性id
    jfieldID field = (*env)->GetStaticFieldID(env, jcs, "helloString", "Ljava/lang/String;");

    //函数原型jobject(JNICALL *GetStaticObjectField)(JNIEnv *env, jclass clazz, jfieldID fieldID);
    //第一个参数
    //第二个参数 对应静态变量对应类的class 本例中FMY
    //第三个参数 静态属性的id
    //返回对应object属性
    jstring fields =(*env)->GetStaticObjectField(env,jcs,field);



    printf("come from java string%s", (*env)->GetStringUTFChars(env, fields, NULL));


    jstring restring = (*env)->NewStringUTF(env, "hello world 鍢垮樋");// 返回的是hello world 嘿嘿 -->>乱码处理 后面我会详细解释


    //函数原型void (JNICALL *SetStaticObjectField)(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value);
    (*env)->SetStaticObjectField(env, jcs, field, restring);

}

输出:
hello world 嘿嘿
come from java string你好世界

访问属性方法

java源码:

package com.fmy;

import java.io.UnsupportedEncodingException;
import java.util.Random;

public class FMY {

    public FMY(){

    }


    static{
        //加载动态链接库
        System.loadLibrary("Project2");
    }

    public static void main(String[] args) {


    FMY fmy = new FMY();
    // 用native方法间接调用getRandom 方法返回
    int i = fmy.callJavaMethod();


    System.out.println(i);  


    }

    //用于调用getRandom方法
    public native int callJavaMethod();

    //产生随机数 让native方法调用
    public int  getRandom(){

        Random random = new Random();

        return random.nextInt();

    }


}

c源码:

#include"com_fmy_FMY.h"
#include<Windows.h>



JNIEXPORT jint JNICALL Java_com_fmy_FMY_callJavaMethod
(JNIEnv * env, jobject jcl){


    //函数原型: jclass (JNICALL *GetObjectClass)(JNIEnv *env, jobject obj);
    //获取对象的class
    jclass obj_class = (*env)->GetObjectClass(env, jcl);

    //函数原型:    jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig);
    //第一个参数
    //第二个参数 方法对应所在对象的class 本例中FMY
    //第三个参数 方法名
    //第四个参数 签名
    //得到方法id:
    jmethodID methodId = (*env)->GetMethodID(env, obj_class, "getRandom", "()I");


    //函数原型:jobject(JNICALL *CallObjectMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...);
    //第一个参数
    //第二个参数 方法所在对象的实例
    //第三个参数 方法id
    //第四个参数 可以变参数 为传入参数
    jint result =(*env)->CallObjectMethod(env,jcl,methodId);

    return result;

}


访问java静态属性方法

java 源码:

package com.fmy;

import java.io.UnsupportedEncodingException;
import java.util.Random;

public class FMY {

    public FMY(){

    }


    static{
        //加载动态链接库
        System.loadLibrary("Project2");
    }

    public static void main(String[] args) {



    // 用native方法间接调用getRandom 方法返回
    int i = callJavaMethod();


    System.out.println(i);  


    }

    //用于调用getRandom方法
    public native static int callJavaMethod();

    //产生随机数 让native方法调用
    public static int  getRandom(){

        Random random = new Random();

        return random.nextInt();

    }


}

c实现类:

#include"com_fmy_FMY.h"
#include<Windows.h>



JNIEXPORT jint JNICALL Java_com_fmy_FMY_callJavaMethod
(JNIEnv * env, jclass jcl){

    //函数原型:    jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig);
    //第一个参数
    //第二个参数 方法对应所在对象的class 本例中FMY
    //第三个参数 方法名
    //第四个参数 签名
    //得到方法id:
    jmethodID methodId = (*env)->GetStaticMethodID(env, jcl, "getRandom", "()I");


    //函数原型:jobject(JNICALL *CallObjectMethod)(JNIEnv *env, jobject obj, jmethodID methodID, ...);
    //第一个参数
    //第二个参数 方法所在对象的实例
    //第三个参数 方法id
    //第四个参数 可以变参数 为传入参数
    jint result = (*env)->CallStaticIntMethod(env, jcl,methodId);

    return result;

}


创建一个实例类

java:

package com.fmy;

import java.io.UnsupportedEncodingException;
import java.util.Random;

public class FMY {

    String  name;

    int age;

    public FMY(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }


    public native static FMY newObjfromJni();

    static{
        //加载动态链接库
        System.loadLibrary("Project2");
    }

    public static void main(String[] args) {

        FMY newObjfromJni = newObjfromJni();

        System.out.println(newObjfromJni());
    }


    public String toString() {
        return "FMY [name=" + name + ", age=" + age + "]";
    }



}

C语言实现:

#include"com_fmy_FMY.h"
#include<Windows.h>


JNIEXPORT jobject JNICALL Java_com_fmy_FMY_newObjfromJni
(JNIEnv * env, jclass jcl){


    //函数原型jclass(JNICALL *FindClass)(JNIEnv *env, const char *name);
    //得到指定的class
    //第一个参数
    //第二个参数  要获取class 的包名/类名
    jclass fmy_class = (*env)->FindClass(env, "com/fmy/FMY");

    //函数原型    jmethodID (JNICALL *GetMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig);
    //第一个参数 
    //第二个参数 对应的 所在类名class
    //第三个参数 方法名 构造方法固定为<init>
    //第四个参数 签名
    jmethodID method_id = (*env)->GetMethodID(env, fmy_class, "<init>", "(Ljava/lang/String;I)V");

    jstring name = (*env)->NewStringUTF(env,"XiaoMing");

    jint age = 12l;

    //函数原型:    jobject (JNICALL *NewObject)(JNIEnv *env, jclass clazz, jmethodID methodID, ...);
    //第一个参数
    //第二个参数 对应类class
    //第三个参数 方法id
    //第四个参数 可变参数 传入构造方法使用
    return  (*env)->NewObject(env, fmy_class, method_id, name, age);

}


调用一个实力类父类的方法

假设一个类叫father 继承Grandpa内部一个方法eat(),子类child继承自father.这时候我们创建一个child对象,然后我们想child对象调用eat方法实行的是Grandpa方法

Grandpa代码:

package com.fmy;

public class Grandpa {
    public void eat(){
        System.out.println("我是爷爷 正在喝酒");
    }
}

Father代码:

package com.fmy;

public class Father extends Grandpa{
    public void eat(){
        System.out.println("我是父亲 正在喝酒");
    }
}

Child:

package com.fmy;

import java.util.Arrays;
import java.util.stream.Stream;

public class Child extends Father {
    @Override
    public void eat() {
         System.out.println("我是孩子 我在喝饮料");
    }
}

Main方法:

package com.fmy;

import java.io.UnsupportedEncodingException;
import java.util.Random;

public class FMY {


    Child child;

    public native  void callFathorMethod();

    static{
        //加载动态链接库
        System.loadLibrary("Project2");
    }

    public static void main(String[] args) {

        FMY fmy = new FMY();

        fmy.child = new Child();

        fmy.callFathorMethod();
    }

}

c代码的实现:

#include"com_fmy_FMY.h"
#include<Windows.h>


JNIEXPORT void JNICALL Java_com_fmy_FMY_callFathorMethod
(JNIEnv * env, jobject jcas){

    //获取对象class 
    jclass fmy_clas = (*env)->GetObjectClass(env, jcas);
    //获取属性id
    jfieldID child_id = (*env)->GetFieldID(env, fmy_clas, "child", "Lcom/fmy/Child;");
    //获取属性引用
    jobject child_obj = (*env)->GetObjectField(env, jcas, child_id);
    //这里写爷爷 包名。类名 那么 调用CallNonvirtualObjectMethod时候那么会调用 爷爷的eat 
    // 如果这里写 com/fmy/father 那么调用父亲的eat
    jclass child_class2 = (*env)->FindClass(env, "com/fmy/Grandpa");
    //得到方法id
    jmethodID methodid = (*env)->GetMethodID(env, child_class2, "eat", "()V");

    //获取调用对象class //我发现这里第二个参数 随便写一个存在的类的包名类名,都可以调用CallNonvirtualObjectMethod
    //并且调用对应的child_class2方法 
    // 如果谁知道的为什么 的话还请告诉我
    jclass child_class1 = (*env)->FindClass(env, "com/fmy/Grandpa");
    //调用爷爷 的eat方法
    (*env)->CallNonvirtualObjectMethod(env, child_obj, child_class1, methodid);


}


java传入数组并对其排序
  • java层源码:
package com.fmy;


public class FMY {

    Child child;

    static{
        //加载动态链接库
        System.loadLibrary("Project2");
    }

    public static void main(String[] args) {

        int [] jintArray = {2,01,2,2,-1,-23,23,5,1};

        FMY fmy = new FMY();

        //利用c对java数组排序  
        fmy.sortJavaIntArray(jintArray);


        System.out.println("排序后");
        for (int i : jintArray) {

            System.out.print(i+" ");
        }
        System.out.println();


    }

    //利用c对java数组排序
    public  native void sortJavaIntArray(int [] arg);


}
  • c层
#include"com_fmy_FMY.h"
#include<Windows.h>
//比较器用于下面qsrot方法使用
int compare(const jint * a, const jint* b){

    return (*a) - (*b);
}
JNIEXPORT void JNICALL Java_com_fmy_FMY_sortJavaIntArray
(JNIEnv * env, jobject jobj, jintArray jiarray){

    jboolean flag ;

    //jint * (JNICALL *GetIntArrayElements)(JNIEnv *env, jintArray array, jboolean *isCopy);
    //第一个参数
    //第二个参数 java的int数组
    //第三个参数 如果内部拷贝了数组返回 此传入参数的数值为true 反之
    jint * mjintp = (*env)->GetIntArrayElements(env, jiarray, &flag);

    printf("is copy array ?%d",flag);

    int len = (*env)->GetArrayLength(env, jiarray);

    //功 能: 使用快速排序例程进行排序
    //函数指针void qsort(void*base, size_t num, size_t width, int(__cdecl*compare)(const void*, const void*));
    //第一个参数 数组指针
    //第二个参数 数组大小
    //第三个参数 各元素的占用空间大小
    //第四个参数 函数指针 用于比较两个数谁比较大
    qsort(mjintp, len, sizeof(jint), compare);

    //同步排序后的数组mjintp到java中的数组jiarray中,因为数组传入到c获取的时候是拷贝了一份 而不是直接引用
    //释放数组的元素
    //mode参数
    //0,Java数组进行更新,并且释放C/C++数组
    //JNI_ABORT,Java数组不进行更新,但是释放C/C++数组
    //JNI_COMMIT,Java数组进行更新,不释放C/C++数组(函数执行完,数组还是会释放)   
    (*env)->ReleaseIntArrayElements(env, jiarray, mjintp,0);
}

  • 输出
排序后
-23 -1 1 1 2 2 2 5 23 
is copy array ?1
JNI创建int数组返回
  • java源码
package com.fmy;


public class FMY {


    Child child;


    static{
        //加载动态链接库
        System.loadLibrary("Project2");
    }

    public static void main(String[] args) {


        FMY fmy = new FMY();

        int[] javaLongArray = fmy.javaIntArray(12);

        for (int i : javaLongArray) {

            System.out.print(i+" ");
        }


    }

    //利用c创建一个len长度的int数组
    public  native int[] javaIntArray(int len);

}
  • c源码
#include"com_fmy_FMY.h"
#include<Windows.h>

JNIEXPORT jintArray JNICALL Java_com_fmy_FMY_javaIntArray
(JNIEnv * env, jobject jobj ,jint len){

    //jintArray(JNICALL *NewIntArray)(JNIEnv *env, jsize len);
    //第一个参数
    //第二个参数 创建数组的长度
    jintArray jntarray= (*env)->NewIntArray(env,len);

    //数组转化为 jint* 类型
    jint * jintp=(*env)->GetIntArrayElements(env,jntarray,NULL);


    for (int i = 0; i < len; i++)
    {   //jint 本质long 所以可以赋值
        jintp[i] = i*10;
    }

    //释放同步
    (*env)->ReleaseIntArrayElements(env,jntarray,jintp,0);

    //返回
    return jntarray;

}

  • 输出
0 10 20 30 40 50 60 70 80 90 100 110 
JNI创建对象数组返回
  • java 源码:
package com.fmy;

public class FMY {

    Child child;

    static{
        //加载动态链接库
        System.loadLibrary("Project2");
    }

    public static void main(String[] args) {


        FMY fmy = new FMY();

        //得到c创建的UUI的数组
        UUID[] javaUUIDArray = fmy.javaStringArray(10);

        for (UUID uuid : javaUUIDArray) {
            System.out.println(uuid);   
        }
    }

    //利用c创建一个UUID对象数组返回
    public  native UUID[] javaStringArray(int len);

}
  • c源码
package com.fmy;

public class FMY {

    Child child;

    static{
        //加载动态链接库
        System.loadLibrary("Project2");
    }

    public static void main(String[] args) {


        FMY fmy = new FMY();

        //得到c创建的UUI的数组
        UUID[] javaUUIDArray = fmy.javaStringArray(10);

        for (UUID uuid : javaUUIDArray) {
            System.out.println(uuid);           
        }

    }

    //利用c创建一个UUID对象数组返回
    public  native UUID[] javaStringArray(int len);

}
  • 输出
7701aa73-0f82-4bd9-aeb6-394ee35a0aa1
55aff859-01e2-4e44-bc1a-1768ba104724
63e62a4b-4a7b-4a51-b48b-269b6ed35c28
a3aa64e9-e7dd-4bc1-a8c5-07235ad665db
fb5cc736-5f8b-4be4-837e-0cf3046a0d38
e4f1195e-0e51-4724-854b-0f42a86df2e8
b4045486-0d77-431c-9312-fced7778c11d
83812826-eb5e-481a-b03a-9415e70afff3
f1796aa8-52b2-474a-a409-dfae4f303c88
343196e7-23f7-4119-81ec-909d58db7eb0
JNI局部引用和全局引用
//JNI引用变量
//C/C++代码必须告知Java虚拟机,什么时候能回收一个对象,什么时候不能回收这个对象
//引用的类型:局部引用和全局引用

//局部引用
//局部引用会在C/C++代码执行完成之后自动释放(可以回收)
//但是,有时候我们需要手动去释放
//1.访问一个很大的Java对象,使用完之后,还要进行复杂的耗时操作
//2.创建了大量的局部引用,占用了太多的内存,而且这些布局引用跟后面的操作没有关联性

//模拟,循环创建对象数组
JNIEXPORT void JNICALL Java_com_tz_jni_TestNative_localRef(JNIEnv * env, jobject obj){
    int i = 0;
    for (; i < 5; i++){
        jclass cls = (*env)->FindClass(env, "java/util/Date");
        jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
        //实例化Date对象
        jobject obj = (*env)->NewObject(env, cls, constructor_mid);
        //Date对象数组
        jobjectArray jobj_arr = (*env)->NewObjectArray(env, 5, cls, obj);
        //提前释放,不要占用内存太久
        //告诉虚拟机垃圾回收器,可以回收这些对象
        (*env)->DeleteLocalRef(env, obj);
        (*env)->DeleteLocalRef(env, jobj_arr);

        //processing 假装这里有很多代码
    }
}

//注意:局部引用不能在多个线程间传递

//全局引用
//可以跨越多个线程,在程序员手动释放之前,一直有效
jstring global_str;

//设置global_str
JNIEXPORT void JNICALL Java_com_tz_jni_TestNative_createGlobalRef(JNIEnv * env, jobject j_obj){
    jstring obj = (*env)->NewStringUTF(env, "jni development is powerful!");
    global_str = (*env)->NewGlobalRef(env, obj);
}

//访问global_str
JNIEXPORT jstring JNICALL Java_com_tz_jni_TestNative_getGlobalRef(JNIEnv * env, jobject obj){
    return global_str;
}

//释放global_str
JNIEXPORT void JNICALL Java_com_tz_jni_TestNative_deleteGlobalRef(JNIEnv * env, jobject obj){
    (*env)->DeleteGlobalRef(env, global_str);
}
JNI抛出异常

这里我们故意在c中写错一些参数让其抛出异常看看java层是否能捕捉到

  • java层
package com.fmy;

public class FMY {


    Child child;


    static{
        //加载动态链接库
        System.loadLibrary("Project2");
    }

    public static void main(String[] args) {

        FMY fmy = new FMY();

        try {
            //使用方法内容故意写错抛出异常
            fmy.exception();
            //特别注意要使用Throwable才能捕获到异常
        } catch (Throwable e) {

            System.out.println("Throwable捕获到异常");
            e.printStackTrace();
        }

        try {
            //使用方法内容故意写错抛出异常
            fmy.exception();
            //特别注意要使用Exception不能捕获到异常
        } catch (Exception e) {

            System.out.println("Exception捕获到异常");
            e.printStackTrace();
        }
    }

    //内部故意写错抛出异常
    public  native void exception();

}
  • c 层
#include"com_fmy_FMY.h"
#include<Windows.h>
JNIEXPORT void JNICALL Java_com_fmy_FMY_exception
(JNIEnv * env, jobject obj){

    jclass cls = (*env)->GetObjectClass(env,obj);
    //其实内部没有chid2这个属性变量所以 会发生异常
    jfieldID myjfieldID=(*env)->GetFieldID(env, cls, "child2", "()V");
    (*env)->GetObjectField(env, obj, myjfieldID);


}

运行结果:

Throwable捕获到异常
java.lang.NoSuchFieldError: child
    at com.fmy.FMY.exception(Native Method)
    at com.fmy.FMY.main(FMY.java:28)
Exception in thread "main" java.lang.NoSuchFieldError: child
    at com.fmy.FMY.exception(Native Method)
    at com.fmy.FMY.main(FMY.java:38)

所以证明:只有Throwable能捕获到异常

那么我们来看看如果手动在c里面抛出去一个异常呢

  • java:
package com.fmy;

public class FMY {


    Child child;


    static{
        //加载动态链接库
        System.loadLibrary("Project2");
    }

    public static void main(String[] args) {

        FMY fmy = new FMY();

        try {
            //使用方法内容故意写错抛出异常
            fmy.exception();
            //特别注意要使用Throwable才能捕获到异常
        } catch (Throwable e) {

            System.out.println("Throwable捕获到异常");
            e.printStackTrace();
        }

        try {
            //使用方法内容故意写错抛出异常
            fmy.exception();
            //特别注意要使用Exception不能捕获到异常
        } catch (Exception e) {

            System.out.println("Exception捕获到异常");
            e.printStackTrace();
        }
    }

    //内部故意写错抛出异常
    public  native void exception();

}
  • c 代码:
#include"com_fmy_FMY.h"
#include<Windows.h>
JNIEXPORT void JNICALL Java_com_fmy_FMY_exception
(JNIEnv * env, jobject obj){


    jclass d = (*env)->FindClass(env, "java/lang/IllegalArgumentException");

    (*env)->ThrowNew(env,d,"来自c语言的异常");
}
Throwable捕获到异常
java.lang.IllegalArgumentException: 4×ÔcÓïÑԵÄÒ쳣
Exception捕获到异常
    at com.fmy.FMY.exception(Native Method)
    at com.fmy.FMY.main(FMY.java:28)
java.lang.IllegalArgumentException: 4×ÔcÓïÑԵÄÒ쳣
    at com.fmy.FMY.exception(Native Method)
    at com.fmy.FMY.main(FMY.java:38)

//乱码原因 我就不解释了 我的文章中有详细说明JNI乱码处理

c层手动抛出的异常可以用exception捕获

缓存策略
  • c源码
#include"com_fmy_FMY.h"
#include<Windows.h>

//缓存机制
JNIEXPORT void JNICALL Java_com_fmy_FMY_cashStrategy
(JNIEnv * env, jobject obj){

    static jstring cashString ;

    if (cashString == NULL)
    {
        printf("creat\n");

        cashString = (*env)->NewStringUTF(env,"测试");

    }else{
        printf("notcreat\n");

    }

}
  • java
package com.fmy;


public class FMY {


    static{
        //加载动态链接库
        System.loadLibrary("Project2");
    }

    public static void main(String[] args) {

        FMY fmy = new FMY();

        for (int i = 0; i < 30; i++) {
            fmy.cashStrategy();
        }

    }

    //缓存
    public  native void cashStrategy();

}
  • 输出
creat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat
notcreat

弱全局引用
节省内存,在内存不足时可以是释放所引用的对象
可以引用一个不常用的对象,如果为NULL,临时创建
创建:NewWeakGlobalRef,销毁:DeleteGlobalWeakRef

package com.fmy;



public class FMY {


    static{
        //加载动态链接库
        System.loadLibrary("Project2");
    }

    public static void main(String[] args) {

        FMY fmy = new FMY();


        fmy.cashStrategy();
        //显示告诉jvm释放内存,但我发现我在调用fmy.cashStrategy();还是不为空 可能是由jvm决定什么回收吧
        fmy.releaseStrategy();
        //notcreat
        fmy.cashStrategy();
    }

    //缓存全局
    public  native String cashStrategy();


    //释放全局弱引用缓存
    public  native void releaseStrategy();




}
  • c
#include"com_fmy_FMY.h"
#include<Windows.h>

jstring cash_string;
//缓存机制
JNIEXPORT jstring JNICALL Java_com_fmy_FMY_cashStrategy
(JNIEnv * env, jobject obj){

    if (cash_string == NULL)
    {
        printf("creat\n");

        cash_string = (*env)->NewWeakGlobalRef(env, (*env)->NewStringUTF(env, "测试"));

    }else{

        printf("notcreat\n");

    }
    return cash_string;
}




JNIEXPORT void JNICALL Java_com_fmy_FMY_releaseStrategy
(JNIEnv * env, jobject jobjt){

    (*env)->DeleteGlobalRef(env, cash_string);

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值