jni使用语法

概述jni(Java Native Interface)是什么?

它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。
jni主要解决java限制的事情,或者要进行高效开发。

最简单的jni使用

package com.dongnaoedu.jni;

public class JniTest {
    public native static String getStringFromC();   
    public static void main(String[] args) {
        String text = getStringFromC();
        System.out.println(text);
    }   
    //加载动态库
    static{
        System.loadLibrary("jni_demo");
    }
}

头文件:

#include <jni.h>
#ifndef _Included_com_dongnaoedu_jni_JniTest
#define _Included_com_dongnaoedu_jni_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_dongnaoedu_jni_JniTest
 * Method:    getStringFromC
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_getStringFromC
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

实现:


#include "com_dongnaoedu_jni_JniTest.h"

JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_getStringFromC
(JNIEnv *env, jclass jcls){
    //JNIEnv 在C++中就是一个结构体的别名
    //env 在C++中一个结构体的一级指针
    return env->NewStringUTF("C String");
}

注意的细节

具体介绍:

每个native函数,都至少有两个参数(JNIEnv*,jclass或者jobject)

  • 1)当native方法为静态方法时:
    jclass 代表native方法所属类的Class对象(JniTest.class)
  • 2)当native方法为非静态方法时:
    jobject 和一个数组和java.lang.Object类或它的子类的实例对应

基本数据

  • Java基本数据类型与JNI数据类型的映射关系
  • Java类型->JNI类型->C类型

基本数据类型

基本类型
    boolean jboolean
    byte jbyte;
    char jchar;
    short jshort;
    int jint;
    long jlong;
    float jfloat;
    double jdouble;
    void void
引用类型(对象)
    String jstring
    object jobject
数组,基本数据类型的数组
    byte[] jByteArray
对象数组
    object[](String[]) jobjectArray
    


java代码:

package com.dongnaoedu.jni;

import java.util.Date;
import java.util.Random;
import java.util.UUID;

import javax.sql.rowset.CachedRowSet;

public class JniTest {

    public String key = "jason";
    
    public Human human = new Man(); 
    
    public static int count = 9;
    
    public native static String getStringFromC();
    
    public native String getString2FromC(int i);
    //访问属性,返回修改之后的属性内容
    public native String accessField();
    
    public native void accessStaticField();
    
    public native void accessMethod();
    
    public native void accessStaticMethod();
    
    public native Date accessConstructor();
    
    public native void accessNonvirtualMethod();
    
    public native String chineseChars(String in);
    
    public native void giveArray(int[] array);
    
    public native int[] getArray(int len);
    
    public native void localRef();
    
    public native void createGlobalRef();
    
    public native String getGlobalRef();
    
    public native void deleteGlobalRef();
    
    public native void exeception();
    
    public native void cached();
    
    public native static void initIds();
    
    public static void main(String[] args) {
        String text = getStringFromC();
        System.out.println(text);
        JniTest t = new JniTest();
        text = t.getString2FromC(6);
        System.out.println(text);
        
        System.out.println("key修改前:"+t.key);
        t.accessField();
        System.out.println("key修改后:"+t.key);
        
        System.out.println("count修改前:"+count);
        t.accessStaticField();
        System.out.println("count修改后:"+count);
        
        t.accessMethod();
        t.accessStaticMethod();
        
        t.accessConstructor();
        
        t.accessNonvirtualMethod();
        
        System.out.println(t.chineseChars("宋喆"));
        
        int[] array = {9,100,10,37,5,10};
        //排序
        t.giveArray(array);
        for (int i : array) {
            System.out.println(i);
        }
        
        //----------
        int[] array2 = t.getArray(10);
        System.out.println("------------");
        for (int i : array2) {
            System.out.println(i);
        }
        
        System.out.println("------08-17------");
        t.createGlobalRef();
        System.out.println(t.getGlobalRef());
        //用完之后释放
        t.deleteGlobalRef();
        System.out.println("释放完了...");
        //System.out.println(t.getGlobalRef());
        
        try {
            t.exeception();                     
        } catch (Exception e) {
            System.out.println("发生异常:"+e.getMessage());
        }
        
        System.out.println("--------异常发生之后-------");
        
        try {
            t.exeception();
        } catch (Exception e) {
            //e.printStackTrace();
            System.out.println(e.getMessage());
        }
        
        //不断调用cached方法
        for (int i = 0; i < 100; i++) {
            t.cached();
        }
    }
    
    //产生指定范围的随机数
    public int genRandomInt(int max){
        System.out.println("genRandomInt 执行了...");
        return new Random().nextInt(max); 
    }
    
    //产生UUID字符串
    public static String getUUID(){
        return UUID.randomUUID().toString();
    }
    
    //加载动态库
    static{ 
        System.loadLibrary("jni_study");
        initIds();
    }

}

#define _CRT_SECURE_NO_WARNINGS
#include "com_dongnaoedu_jni_JniTest.h"
#include <string.h>

//返回字符串
JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_getStringFromC
(JNIEnv *env, jclass jcls){
    return (*env)->NewStringUTF(env,"C String");
}

JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_getString2FromC
(JNIEnv *env, jobject jobj, jint num){
    return (*env)->NewStringUTF(env,"C String2");
}
//C/C++访问Java的成员

//修改属性key
JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_accessField(JNIEnv *env, jobject jobj){
    jclass cls = (*env)->GetObjectClass(env, jobj);//得到JniTest.class对象
    jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");//得到字段jfieldID
    jstring jstr = (*env)->GetObjectField(env, jobj, fid);//获取jfieldID对应字段的属性值
    
    char *c_str = (*env)->GetStringUTFChars(env,jstr,JNI_FALSE);//将字符串转化
    
    //拼接得到新的字符串
    char text[20] = "super ";
    strcat(text,c_str);
    
    jstring new_jstr = (*env)->NewStringUTF(env, text); //c字符串 ->jstring

    (*env)->SetObjectField(env, jobj, fid, new_jstr);//设置字段的值

    return new_jstr;
}

//访问静态属性
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_accessStaticField(JNIEnv *env, jobject jobj){

    jclass cls = (*env)->GetObjectClass(env, jobj);//得到JniTest.class对象
    jfieldID fid = (*env)->GetStaticFieldID(env, cls, "count", "I");//jfieldID

    jint count = (*env)->GetStaticIntField(env, cls, fid);  //GetStatic<Type>Field
    count++;
    
    (*env)->SetStaticIntField(env,cls,fid,count);//SetStatic<Type>Field
}

//2.访问java方法
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_accessMethod(JNIEnv *env, jobject jobj){
    jclass cls = (*env)->GetObjectClass(env, jobj);
    jmethodID mid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I");//jmethodID
    jint random = (*env)->CallIntMethod(env, jobj, mid, 200);//Call<Type>Method
}

//静态方法
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_accessStaticMethod(JNIEnv *env, jobject jobj){

    jclass cls = (*env)->GetObjectClass(env, jobj);//jclass
    jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");//jmethodID  
    
    jstring uuid = (*env)->CallStaticObjectMethod(env, cls, mid);//CallStatic<Type>Method
    
    //isCopy JNI_FALSE,代表java和c操作的是同一个字符串
    char *uuid_str = (*env)->GetStringUTFChars(env, uuid, JNI_FALSE);
}



-----------------------------------------------------------------

//访问构造方法
//使用java.util.Date产生一个当前的时间戳
JNIEXPORT jobject JNICALL Java_com_dongnaoedu_jni_JniTest_accessConstructor
(JNIEnv *env, jobject jobj){
    jclass cls = (*env)->FindClass(env, "java/util/Date");
    //jmethodID
    jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
    //实例化一个Date对象
    jobject date_obj = (*env)->NewObject(env, cls, constructor_mid);
    //调用getTime方法
    jmethodID mid = (*env)->GetMethodID(env, cls, "getTime", "()J");
    jlong time = (*env)->CallLongMethod(env, date_obj, mid);

    printf("\ntime:%lld\n",time);

    return date_obj;
}

//调用父类的方法
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_accessNonvirtualMethod
(JNIEnv *env, jobject jobj){
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //获取man属性(对象)
    jfieldID fid = (*env)->GetFieldID(env, cls, "human", "Lcom/dongnaoedu/jni/Human;");
    //获取
    jobject human_obj = (*env)->GetObjectField(env, jobj, fid);

    //执行sayHi方法
    jclass human_cls = (*env)->FindClass(env, "com/dongnaoedu/jni/Human"); //注意:传父类的名称
    jmethodID mid = (*env)->GetMethodID(env, human_cls, "sayHi", "()V");

    //执行
    //(*env)->CallObjectMethod(env, human_obj, mid);
    //调用的父类的方法
    (*env)->CallNonvirtualObjectMethod(env, human_obj, human_cls, mid);
}

//中文问题
JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_chineseChars
(JNIEnv *env, jobject jobj, jstring in){
    char *c_str = "马蓉与宋江";
    //char c_str[] = "马蓉与宋喆";
    //jstring jstr = (*env)->NewStringUTF(env, c_str);
    //执行String(byte bytes[], String charsetName)构造方法需要的条件
    //1.jmethodID
    //2.byte数组
    //3.字符编码jstring

    jclass str_cls = (*env)->FindClass(env, "java/lang/String");
    jmethodID constructor_mid = (*env)->GetMethodID(env, str_cls, "<init>", "([BLjava/lang/String;)V");

    //jbyte -> char 
    //jbyteArray -> char[]
    jbyteArray bytes = (*env)->NewByteArray(env, strlen(c_str));
    //byte数组赋值
    //0->strlen(c_str),从头到尾
    //对等于,从c_str这个字符数组,复制到bytes这个字符数组
    (*env)->SetByteArrayRegion(env, bytes, 0, strlen(c_str), c_str);

    //字符编码jstring
    jstring charsetName = (*env)->NewStringUTF(env, "GB2312");

    //调用构造函数,返回编码之后的jstring
    return (*env)->NewObject(env,str_cls,constructor_mid,bytes,charsetName);
}

int compare(int *a,int *b){
    return (*a) - (*b);
}

//传入
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_giveArray
(JNIEnv *env, jobject jobj, jintArray arr){
    //jintArray -> jint指针 -> c int 数组
    jint *elems = (*env)->GetIntArrayElements(env, arr, NULL);

    //数组的长度
    int len = (*env)->GetArrayLength(env, arr);
    //排序
    qsort(elems, len, sizeof(jint), compare);   

    //同步
    //mode
    //0, Java数组进行更新,并且释放C/C++数组
    //JNI_ABORT, Java数组不进行更新,但是释放C/C++数组
    //JNI_COMMIT,Java数组进行更新,不释放C/C++数组(函数执行完,数组还是会释放)
    (*env)->ReleaseIntArrayElements(env, arr, elems, JNI_COMMIT);
}

//返回数组
JNIEXPORT jintArray JNICALL Java_com_dongnaoedu_jni_JniTest_getArray(JNIEnv *env, jobject jobj, jint len){
    //创建一个指定大小的数组
    jintArray jint_arr = (*env)->NewIntArray(env, len);
    jint *elems = (*env)->GetIntArrayElements(env, jint_arr, NULL); 
    int i = 0;
    for (; i < len; i++){
        elems[i] = i;
    }

    //同步
    (*env)->ReleaseIntArrayElements(env, jint_arr, elems, 0);   

    return jint_arr;
}


//JNI 引用变量
//引用类型:局部引用和全局引用
//作用:在JNI中告知虚拟机何时回收一个JNI变量

//局部引用,通过DeleteLocalRef手动释放对象
//1.访问一个很大的java对象,使用完之后,还要进行复杂的耗时操作
//2.创建了大量的局部引用,占用了太多的内存,而且这些局部引用跟后面的操作没有关联性

//模拟:循环创建数组
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_localRef(JNIEnv *env, jobject jobj){
    int i = 0;
    for (; i < 5; i++){
        //创建Date对象
        jclass cls = (*env)->FindClass(env, "java/util/Date");
        jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
        jobject obj = (*env)->NewObject(env, cls, constructor_mid);
        //此处省略一百行代码...

        //不在使用jobject对象了
        //通知垃圾回收器回收这些对象
        (*env)->DeleteLocalRef(env, obj);
        //此处省略一百行代码...
    }
}


//全局引用
//共享(可以跨多个线程),手动控制内存使用
jstring global_str;

//创建
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_createGlobalRef(JNIEnv *env, jobject jobj){
    jstring obj = (*env)->NewStringUTF(env, "jni development is powerful!");
    global_str = (*env)->NewGlobalRef(env, obj);
}

//获得
JNIEXPORT jstring JNICALL Java_com_dongnaoedu_jni_JniTest_getGlobalRef(JNIEnv *env, jobject jobj){
    return global_str;
}

//释放
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_deleteGlobalRef(JNIEnv *env, jobject jobj){
    (*env)->DeleteGlobalRef(env, global_str);
}

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

//异常处理
//1.保证Java代码可以运行
//2.补救措施保证C代码继续运行

//JNI自己抛出的异常,在Java层无法被捕捉,只能在C层清空
//用户通过ThrowNew抛出的异常,可以在Java层捕捉
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_exeception(JNIEnv *env, jobject jobj){
    jclass cls = (*env)->GetObjectClass(env, jobj);
    jfieldID fid = (*env)->GetFieldID(env, cls, "key2", "Ljava/lang/String;");
    //检测是否发生Java异常
    jthrowable exception = (*env)->ExceptionOccurred(env);
    if (exception != NULL){
        //让Java代码可以继续运行
        //清空异常信息
        (*env)->ExceptionClear(env);

        //补救措施
        fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
    }

    //获取属性的值
    jstring jstr = (*env)->GetObjectField(env, jobj, fid);
    char *str = (*env)->GetStringUTFChars(env, jstr, NULL);

    //对比属性值是否合法
    if (_stricmp(str, "super jason") != 0){
        //认为抛出异常,给Java层处理
        jclass newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException");
        (*env)->ThrowNew(env,newExcCls,"key's value is invalid!");
    }
}

//缓存策略

//static jfieldID key_id 
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_cached(JNIEnv *env, jobject jobj){
    jclass cls = (*env)->GetObjectClass(env, jobj); 
    //获取jfieldID只获取一次
    //局部静态变量
    static jfieldID key_id = NULL;
    if (key_id == NULL){
        key_id = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");
        printf("--------GetFieldID-------\n");
    }
}

//初始化全局变量,动态库加载完成之后,立刻缓存起来
jfieldID key_fid;
jmethodID random_mid;
JNIEXPORT void JNICALL Java_com_dongnaoedu_jni_JniTest_initIds(JNIEnv *env, jclass jcls){   
    key_fid = (*env)->GetFieldID(env, jcls, "key", "Ljava/lang/String;");
    random_mid = (*env)->GetMethodID(env, jcls, "genRandomInt", "(I)I");
}


小结基础:

jclass对象是java中对象所对应的Class对象
jobject对象是java中的对象实体

得到jclass的方式:
1.jclass cls = (*env)->GetObjectClass(env, jobj);
2.jclass cls = (*env)->FindClass(env, "java/util/Date");

得到jobject对象的方式:
jclass cls = (*env)->FindClass(env, "java/util/Date");
jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
jobject date_obj = (*env)->NewObject(env, cls, constructor_mid);//实例化一个Date对象


得到jfieldID方式:
1.jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;");//非静态
2.jfieldID fid = (*env)->GetStaticFieldID(env, cls, "count", "I");//静态


得到jmethodID方式:
1.jmethodID mid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I");//jmethodID
2.jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");//jmethodID    


通过jfieldID找到对应的字段值方式:
1.jstring jstr = (*env)->GetObjectField(env, jobj, fid);
2.jint count = (*env)->GetStaticIntField(env, cls, fid);    //GetStatic<Type>Field

通过jmethodID找到对应方法的方式:
1.jint random = (*env)->CallIntMethod(env, jobj, mid, 200);//Call<Type>Method
2.jstring uuid = (*env)->CallStaticObjectMethod(env, cls, mid);//CallStatic<Type>Method


派生类调用父类方法:
jclass cls = (*env)->GetObjectClass(env, jobj);
jfieldID fid = (*env)->GetFieldID(env, cls, "human", "Lcom/dongnaoedu/jni/Human;");
jobject human_obj = (*env)->GetObjectField(env, jobj, fid);//得到Human派生类对象
//执行sayHi方法
jclass man_cls = (*env)->FindClass(env, "com/dongnaoedu/jni/Man"); //注意:传父类的名称
jmethodID mid = (*env)->GetMethodID(env, man_cls, "sayHi", "()V");
(*env)->CallNonvirtualObjectMethod(env, human_obj, man_cls, mid);//需要派生类jobject和父类的jclass和父类的jmethodID
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容