(二)JNI 的数据类型以及C访问Java属性和方法

温馨提示

写博客是为了记录在开发过程中所涉及到的技术以及遇到的问题的解决,如果该博客对您有所帮助,希望可以点个关注/喜欢;如果您对文章中的内容有什么不同的见解,欢迎留言进行讨论。谢谢!

JNI 的数据类型

一、JNI 数据类型

Java基本数据类型与JNI数据类型的映射关系

Java数据类型 JNI数据类型 C/C++数据类型
boolean jboolean unsigned char
byte jbyte signed char
char jchar unsigned short
short jshort short
int jint int
long jlong long long
float jfloat float
double jdouble double
void void void

Java引用数据类型与JNI数据类型的映射关系

Java数据类型 JNI数据类型
String jstring
Object jobject
基本数据类型数组如: byte[] jbyteArray
对象数组如: Object[],String[] jobjectArray

二、C通过JNI调用Java

1、访问Java属性

GET<Type>Field

  • 编写JNITest.java文件
public class JNITest {
    public String key = "World!";
    //访问属性,返回修改之后的属性内容
    public native String accessField();

    public static void main(String[] args){
        JNITest t = new JNITest();
        System.out.println("key修改前:"+t.key);
        t.accessField();
        System.out.println("key修改后:"+t.key);
    }

    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_jni_JNITest */

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    accessField
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_accessField
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"

JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_accessField
(JNIEnv *jEnv, jobject jobj) {
    //jobj是t对象,JniTest.class
    jclass cls = (*jEnv)->GetObjectClass(jEnv, jobj);
    //jfieldID
    //属性名称,属性签名
    jfieldID fid = (*jEnv)->GetFieldID(jEnv, cls, "key", "Ljava/lang/String;");

    //World! >> Hello World!
    //获取key属性的值
    //Get<Type>Field
    jstring jstr = (*jEnv)->GetObjectField(jEnv, jobj, fid);
    printf("jstr:%#x\n", &jstr);

    //jstring -> c字符串
    //isCopy 是否复制(true代表复制,false不复制)
    char *c_str = (*jEnv)->GetStringUTFChars(jEnv, jstr, NULL);
    //拼接得到新的字符串
    char text[20] = "Hello ";
    strcat(text, c_str);

    //c字符串 ->jstring
    jstring new_jstr = (*jEnv)->NewStringUTF(jEnv, text);

    //修改key
    //Set<Type>Field
    (*jEnv)->SetObjectField(jEnv, jobj, fid, new_jstr);

    printf("new_jstr:%#x\n", &new_jstr);

    return new_jstr;
}
  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。

2、访问Java静态属性

GET<Type>Field

  • 编写JNITest.java文件
public class JNITest {
    public static int count = 5;
    //访问属性,返回修改之后的属性内容
    public native int accessStaticField();

    public static void main(String[] args){
        JNITest t = new JNITest();
        System.out.println("count 修改前:"+t.count);
        t.accessStaticField();
        System.out.println("count 修改后:"+t.count);
    }

    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h"
/* Header for class com_example_jni_JNITest */

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    accessField
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jint JNICALL Java_com_example_jni_JNITest_accessStaticField
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"

JNIEXPORT jint JNICALL Java_com_example_jni_JNITest_accessStaticField
(JNIEnv *jEnv, jobject jobj) {
    //jobj是t对象,JniTest.class
    jclass cls = (*jEnv)->GetObjectClass(jEnv, jobj);
    //jfieldID
    //属性名称,属性签名
    jfieldID fid = (*jEnv)->GetStaticFieldID(jEnv, cls, "count", "I");

    //获取 count 属性的值
    //Get<Type>Field
    jint count = (*jEnv)->GetStaticIntField(jEnv, jobj, fid);
    printf("jstr:%#x\n", &jstr);
    count++;
    (*jEnv)->SetStaticIntField(jEnv, jobj, fid,count);
    return count;
}
  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。

3、访问java方法

Call<Type>Method

  • 编写JNITest.java文件
public class JNITest {
  
    public native void accessMethod();

    public static void main(String[] args){
        JNITest t = new JNITest();
        t.accessMethod();
    }

    //产生指定范围的随机数
    public int genRandomInt(int max){
        System.out.println("genRandomInt 执行了...");
        return new Random().nextInt(max); 
    }
    
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest

产生的结果中有对应的属性和方法的签名

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

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    accessMethod
 * Signature: (I)I;
 */
JNIEXPORT void JNICALL Java_com_example_jni_JNITest_accessMethod
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"

JNIEXPORT void JNICALL Java_com_example_jni_JNITest_accessMethod
(JNIEnv *env, jobject jobj){
    //jclass
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //jmethodID
    jmethodID mid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I");
    //调用
    //Call<Type>Method
    jint random = (*env)->CallIntMethod(env, jobj, mid, 200);
    printf("random num:%ld",random);

    //.....
}
  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。

4、访问静态方法

CallStatic<Type>Method

  • 编写JNITest.java文件
public class JNITest {
  
    public native void accessStaticMethod();

    public static void main(String[] args){
        JNITest t = new JNITest();
        t.accessStaticMethod();
    }

    //产生UUID字符串
    public static String getUUID(){
        return UUID.randomUUID().toString();
    }
    
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest

产生的结果中有对应的属性和方法的签名

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

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    accessStaticMethod
 * Signature: ()Ljava/lang/String;;
 */
JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_accessStaticMethod
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"

JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_accessStaticMethod
(JNIEnv *env, jobject jobj){
    //jclass
    jclass cls = (*env)->GetObjectClass(env, jobj);
    //jmethodID 
    jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;");
    
    //调用
    //CallStatic<Type>Method
    jstring uuid = (*env)->CallStaticObjectMethod(env, cls, mid);

    //随机文件名称 uuid.txt
    //jstring -> char*
    //isCopy ,代表java和c操作的是同一个字符串
    char *uuid_str = (*env)->GetStringUTFChars(env, uuid, NULL);
    //拼接
    char filename[100];
    sprintf(filename, "D://%s.txt",uuid_str);
    FILE *fp = fopen(filename,"w");
    fputs("How are you?", fp);
    fclose(fp);
}

  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。

5、访问构造方法

通过FindClass找到对应的类

  • 编写JNITest.java文件
public class JNITest {
    //java.util.Date
    public native Date accessConstructor();

    public static void main(String[] args){
        JNITest t = new JNITest();
        t.accessConstructor();
    }

    
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest

产生的结果中有对应的属性和方法的签名

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

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    accessConstructor
 * Signature: 
 */
JNIEXPORT jobject JNICALL Java_com_example_jni_JNITest_accessConstructor
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"

//使用java.util.Date产生一个当前的时间戳
JNIEXPORT jobject JNICALL Java_com_example_jni_JNITest_accessConstructor
(JNIEnv *env, jobject jobj){
    jclass cla = (*env)->FindClass(env,"java/util/Date");
    //jmethodID,构造方法的方法名称固定为"<init>"
    jmethodID constructor_mid = (*env)->GetMethodID(env,cls,"<init>","()V");
    //实例化一个Date对象
    jobject date_obj = (*env)->NewObject(env,cls,constructor_mid);
    //调用getTime方法
    jmehoodID mid = (*env)->GetMethodID(env,cls,"getTime","()J");
    //调用
    jlong time = 
    (*env)->CallLongMethod(env,date_obj,mid);
    //time 为long long类型
    printf("time: %lld\n",time);
    return date_obj;
}

  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。

6、访问父类的方法


  • 编写父类文件
public class Human {
    public void sayHi(){
        System.out.println("Human say Hi...");
    }
}


public class man extends Human{
    @override
    public void sayHi(){
        System.out.println("Man say Hi,How are you?");
    }
}
  • 编写JNITest.java文件
public class JNITest {
    public Human human = new Man();
    public native void accessNonvirtualMethod();

    public static void main(String[] args){
        JNITest t = new JNITest();
        t.accessNonvirtualMethod();
    }

    
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest

产生的结果中有对应的属性和方法的签名

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

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    accessNonvirtualMethod
 * Signature: 
 */
JNIEXPORT jobject JNICALL Java_com_example_jni_JNITest_accessNonvirtualMethod
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"

//调用父类的方法 
JNIEXPORT jobject JNICALL Java_com_example_jni_JNITest_accessNonvirtualMethod
(JNIEnv *env, jobject jobj){
    jclass cls = (*env)->GetObjectClass(env,jobj);
    //获取Man属性
    jfieldID fid =  (*env)->GetFieldID(env,cls,"human","Lcom/example/jni/Human;");
    jobject human_obj = (*env)->GetObjectField(env,jobj,fid);
    //执行sayHi方法
    //传父类的父类
    jclass human_cls = (*env)->FindClass(env,"com/example/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);
    
}

  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。

三、JNI字符串乱码问题

  • 编写JNITest.java文件
public class JNITest {
    
    public native String chineseChars(String in);

    public static void main(String[] args){
        JNITest t = new JNITest();
        String outStr = t.chineseChars("传入一串中文");
        System.out.println(outStr);
    }

    
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest

产生的结果中有对应的属性和方法的签名

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

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    chineseChars
 * Signature: 
 */
JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_chineseChars
(JNIEnv *, jobject,jstring);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"

//中文问题
JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_chineseChars
(JNIEnv *env, jobject jobj, jstring in) {
    char *c_str = (*env)->GetStringUTFChars(env, in, NULL);
    printf("%s\n", c_str);
    char *c_str_out = "传出一段文字";
    //NewStringUTF 是utf-16编码
    jstring j_str = (*env)->NewStringUTF(env, c_str_out);
    //使用Java处理乱码问题
    jclass jcls = (*env)->FindClass(env, "java/lang/String");
    //jmethodID
    jmethodID c_mid = (*env)->GetMethodID(env, jcls, "<init>", "([BLjava/lang/String;)V");
    //byte[]数组
    //jbyteArray -> char[]
    jbyteArray bytes = (*env)->NewByteArray(env, strlen(c_str_out));
    //byte数组赋值
    (*env)->SetByteArrayRegion(env, bytes, 0, strlen(c_str_out), c_str_out);
    //字符编码
    jstring charsetName = (*env)->NewStringUTF(env, "GB2312");
    //调用构造函数,返回编码之后的字符串

    return (*env)->NewObject(env, jcls, c_mid, bytes, charsetName);
}

  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。

四、JNI数组处理,传入一个数组,返回另一个数组

  • 编写JNITest.java文件
public class JNITest {
    
    public native int[] sortArray(int[] array);

    public static void main(String[] args){
        JNITest t = new JNITest();
        int[] arr = new int[]{6,8,1,17,23,12,65,32};
        int newArr = t.sortArray();
        for (int i : arr) {
            System.out.println(i);
        }
        System.out.println("返回的新数组为:");
         for (int i : newArr) {
            System.out.println(i);
        }
    }

    
    static {
        System.loadLibrary("JNIProject");
    }
}
  • 在com_example_jni_JNITest.h文件声明对应的方法;签名可通过下面的命令获取
javap -s -p com.example.jni.JNITest

产生的结果中有对应的属性和方法的签名

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

#ifndef _Included_com_example_jni_JNITest
#define _Included_com_example_jni_JNITest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_JNITest
 * Method:    sortArray
 * Signature: 
 */
JNIEXPORT jintArray JNICALL Java_com_example_jni_JNITest_sortArray
(JNIEnv *, jobject,jintArray);
#ifdef __cplusplus
}
#endif
#endif

  • 在JNITest.c文件实现com_example_jni_JNITest.h中声明的方法
#include "com_example_jni_JNITest.h"
#include <stdlib.h>

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

//数组处理
JNIEXPORT jintArray JNICALL Java_com_example_jni_JNITest_sortArray
(JNIEnv *env, jobject jobj, jintArray arr) {
    //拿到 jintArray 的指针 -> 得到 c int 数组
    jint *elems = (*env)->GetIntArrayElements(env,arr,NULL);
    //数组的长度
    int len = (*env)->GetArrayLength(env,arr);
    
    //排序
    qsort(elems, len, sizeof(jint), compare);
    //同步,第4个参数:
    //JNI_OK(0):Java数组进行更新,并且释放C/C++数组
    //JNI_COMMIT(1):Java数组进行更新,不释放C/C++数组(函数执行完数组会释放)
    //JNI_ABOUT:Java数组不进行更新,但释放C/C++数组
    (*env)->ReleaseIntArrayElements(env, arr, elems,JNI_COMMIT);
    
    
    //将原数组倒序生成一个新的数组
    jintArray jint_new_arr = (*env)->NewIntArray(env,len);
    jint *new_elems = (*env)->GetIntArrayElements(env,jint_new_arr,NULL);
    int i = len-1;
    for(; i >= 0; i--){
        int j = len-1-i;
        new_elems[j] = elems[i];
    }
    //同步
    (*env)->ReleaseIntArrayElements(env, jint_new_arr, new_elems, JNI_COMMIT);
    return jint_new_arr;
}

  • 将c代码生成解决文件(动态库.dll)
  • 将动态库文件拷贝到java项目根路径下
  • 运行JNITest.java ,即可看到输出结果。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,755评论 6 507
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,305评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,138评论 0 355
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,791评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,794评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,631评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,362评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,264评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,724评论 1 315
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,900评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,040评论 1 350
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,742评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,364评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,944评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,060评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,247评论 3 371
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,979评论 2 355

推荐阅读更多精彩内容