JNI編程二:JNI中的數(shù)據(jù)類型

前面我們闡述了JNI的開發(fā)流程,接下來探究JNI中的數(shù)據(jù)類型也在前一篇博客創(chuàng)建的java工程和c++工程里面編寫,沒有看的小伙伴可以去這篇博客里面找http://www.itdecent.cn/p/486331fe30ec

接下來我們開始數(shù)據(jù)類型的探究了

1.數(shù)據(jù)類型 jclass / jobject

在jni里面會(huì)有兩個(gè)這樣的數(shù)據(jù)類型jclass / jobject,顧名思義它們分別對(duì)應(yīng)java里面的 Class和Object。
現(xiàn)在我們來編寫對(duì)應(yīng)的代碼,之前我們創(chuàng)建的java工程里面有一個(gè)類JniMain.java,我們聲明了第一個(gè)native方法。

public class JniMain {

    //靜態(tài)方法
    public native static String getStringFromC();
    
    
    static{
        System.loadLibrary("JNI_Demo1");
    }
    
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        System.out.println(getStringFromC());
    }

}

我們聲明的 public native static String getStringFromC()這個(gè)方法是一個(gè)靜態(tài)方法,這也就意味著我們可以不需要?jiǎng)?chuàng)建JniMain類對(duì)象而直接調(diào)用。

那么再來看看通過javah編譯生成的JniMain.h文件

#include "jni.h"

#ifndef _Included_JniMain
#define _Included_JniMain
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JniMain
 * Method:    getStringFromC
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

我們可以看到Java_JniMain_getStringFromC(JNIEnv*,jclass)函數(shù)里面有一個(gè)參數(shù)是jclass類型的。

再來看看我們實(shí)現(xiàn)JniMain.h文件函數(shù)的jni_impl.c

#include "stdafx.h"

#include "JniMain.h"

JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC (JNIEnv * env, jclass jclz){
    return (*env)->NewStringUTF(env, "hello JNI");
}

函數(shù)NICALL Java_JniMain_getStringFromC (JNIEnv * env, jclass jclz)里面的第二個(gè)參數(shù)是jclass類型,形參jclz就是對(duì)應(yīng)的JniMain.class對(duì)象。

也就是說當(dāng)我們?cè)趈ava文件里面編寫native方法是一個(gè)static修飾的方法,那么我們jni頭文件里面對(duì)應(yīng)的方法就會(huì)添加一個(gè)jclass類型的形參。

接下來咋們?cè)贘niMain.java里面編寫一個(gè)非靜態(tài)native方法 String getStringFromC2()

public class JniMain {

    //靜態(tài)方法
    public native static String getStringFromC();
    
    //非靜態(tài)方法
    public native String getStringFromC2();

}

再來看一看編譯生成的JniMain.h文件

#include <jni.h>

#ifndef _Included_JniMain
#define _Included_JniMain
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JniMain
 * Method:    getStringFromC
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC
  (JNIEnv *, jclass);

/*
 * Class:     JniMain
 * Method:    getStringFromC2
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC2
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

這個(gè)時(shí)候Java_JniMain_getStringFromC2(JNIEnv *, jobject)函數(shù)的形參就變成了jobject類型的了。接下來我們?cè)賮砜纯磈ni_impl.c里面對(duì)該函數(shù)的實(shí)現(xiàn)

#include "stdafx.h"

#include "JniMain.h"

JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC
(JNIEnv * env, jclass jclz){
    return (*env)->NewStringUTF(env, "hello JNI");
}

JNIEXPORT jstring JNICALL Java_JniMain_getStringFromC2
(JNIEnv * env, jobject jobj){
    return (*env)->NewStringUTF(env, "I'm stonger");
}

可以看到Java_JniMain_getStringFromC2(JNIEnv * env, jobject jobj)函數(shù)里面的形參是jobject類型的,形參jobj就是對(duì)應(yīng)的java里面的JniMain對(duì)象。

最后我們將c工程再編譯生成一個(gè)JNI_Demo1.dll動(dòng)態(tài)庫,將它放到j(luò)ava工程里面運(yùn)行。
附上JniMain.java調(diào)用動(dòng)態(tài)庫的代碼

public class JniMain {

    //靜態(tài)方法
    public native static String getStringFromC();
    
    //非靜態(tài)方法
    public native String getStringFromC2();
    
    static{
        System.loadLibrary("JNI_Demo1");
    }
    
    public static void main(String[] args) {
        
        //調(diào)用靜態(tài)native方法
        System.out.println(getStringFromC());
        
        //調(diào)用非靜態(tài)native方法
        JniMain jm = new JniMain();
        System.out.println(jm.getStringFromC2());
        
    }

}

運(yùn)行結(jié)果如下


image.png

2.JNI常見的數(shù)據(jù)類型

現(xiàn)在java JNI c所對(duì)應(yīng)的基本數(shù)據(jù)類型
java ???? JNI ???? c
基本數(shù)據(jù)類型:
boolean ?? jboolean ??unsigned char
byte ???? jbyte ???signed char
char ?? ??jchar ??? signed char
short ??? jshort ???? short
int ????? jint?????int
long ???? jlong ????long long
float ???? jfloat????float
double ???double????double
引用類型:
String ???? jstring
Object ???? jobject
基本數(shù)據(jù)類型數(shù)組:
byte[] ???? jByteArray
引用數(shù)據(jù)類型數(shù)組:
Object[]??? jobjectArray
String[]??? jobjectArray

3.運(yùn)用數(shù)據(jù)類型

接下來,咋們來運(yùn)用運(yùn)用jin數(shù)據(jù)類型。

舉個(gè)例子,在java代碼中有一個(gè)String類型的變量,我們通過jni去修改這個(gè)變量。

現(xiàn)在我們開始寫java代碼,JniMain里面聲明了key字符串和accessFieldModify()方法

public class JniMain {
    
    //一個(gè)全局的字符串
    public String key = "key";
    
    //修改key的方法
    public native String accessFieldModify();
    
    public static void main(String[] args) {
        
    }

}

我們用javah編譯JniMain.java,將生成好的JniMain.h放到c工程里面,下面展示JniMain.h代碼

#include <jni.h>

#ifndef _Included_JniMain
#define _Included_JniMain
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JniMain
 * Method:    accessFieldModity
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_JniMain_accessFieldModity
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

接下來我們就在jni_impl.c文件里面實(shí)現(xiàn)這個(gè)Java_JniMain_accessFieldModity(JNIEnv *, jobject)方法,這個(gè)方法的功能就是修改JniMain.java里面全局變量key的值。實(shí)現(xiàn)代碼如下

#include "stdafx.h"

#include "JniMain.h"
#include <string.h>

//訪問java中非靜態(tài)全局變量key,并修改值
JNIEXPORT jstring JNICALL Java_JniMain_accessFieldModity
(JNIEnv * env, jobject jobj){

    //得到j(luò)class,即JniMain.class
    jclass jclz = (*env)->GetObjectClass(env,jobj);

    //得FieldId, "key" 屬性名稱,"Ljava/lang/String;"屬性簽名
    jfieldID fid = (*env)->GetFieldID(env,"jclz", "key", "Ljava/lang/String;");

    //得到key對(duì)應(yīng)的值
    jstring jstr = (*env)->GetObjectField(env,jobj,fid);

    //將jstring類型轉(zhuǎn)化為c語言的char字符數(shù)組
    char * c_str = (*env)->GetStringUTFChars(env,jstr,NULL);

    //將字符串"key"改成"hello key"
    char text[20] = "hello ";
    strcat(text, c_str);

    jstring new_str = (*env)->NewStringUTF(env,text);

    //給jobj的key成員變量設(shè)置新的值
    (*env)->SetObjectField(env,jobj,fid,new_str);

        //釋放內(nèi)存
    (*env)->ReleaseStringUTFChars(env,new_str,c_str);

    return new_str;
}

上述代碼中的jni方法太糾結(jié),這些方法的作用會(huì)在以后的博客中介紹到,現(xiàn)在只需要結(jié)合注釋明白Java_JniMain_accessFieldModity方法里面的實(shí)現(xiàn)過程即可。其中獲取域jfieldID的方法有個(gè)屬性簽名"Ljava/lang/String;",這個(gè)簽名是標(biāo)識(shí)這個(gè)域在在java里面對(duì)應(yīng)的什么數(shù)據(jù)類型,"Ljava/lang/String;"就代表的是String類型。
關(guān)于屬性前面有一個(gè)對(duì)應(yīng)的表,在平時(shí)編寫jni時(shí)對(duì)應(yīng)查閱一下就行了。

Java屬性簽名列表.png
簽名列表.png

好的,我們編寫玩jni代碼之后就生成對(duì)應(yīng)的.dll動(dòng)態(tài)庫(如何生成動(dòng)態(tài)庫,在上一篇博客里面又講到),再將動(dòng)態(tài)庫放到j(luò)ava工程里面調(diào)用。
貼出JniMain.java里面的調(diào)用代碼:

public class JniMain {
    
    //一個(gè)全局的字符串
    public String key = "key";
    
    //修改key的方法
    public native String accessFieldModify();
    
    
    static{
        System.loadLibrary("JNI_Demo1");
    }
    
    public static void main(String[] args) {
        
        //調(diào)用非靜態(tài)native方法
        JniMain jm = new JniMain();
        
        System.out.println("change before key: "+jm.key);
        jm.accessFieldModify();
        System.out.println("after change key: "+jm.key);
    }

}

再來看看運(yùn)行結(jié)果,成功修改了成員變量key的值


再舉個(gè)例子,在java里面定義一個(gè)int類型的靜態(tài)成員變量,然后通過jni去修改這個(gè)變量。

首先編寫我們的JniMain.java代碼

public class JniMain {
    
    public static int count = 2;
    
    public native void accessStaticFieldModify();
}

接下來,生成JniMain.h,并JniMain.h文件放到c工程里面

#include "jni.h"

#ifndef _Included_JniMain
#define _Included_JniMain
#ifdef __cplusplus
extern "C" {
#endif

/*
 * Class:     JniMain
 * Method:    accessStaticFieldModify
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_JniMain_accessStaticFieldModify
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

然后再在jni_impl.c里面實(shí)現(xiàn)Java_JniMain_accessStaticFieldModify(JNIEnv *, jobject)函數(shù)。

#include "stdafx.h"

#include "JniMain.h"
#include <string.h>

JNIEXPORT void JNICALL Java_JniMain_accessStaticFieldModify
(JNIEnv * env, jobject jobj){

    //得到j(luò)class,即JniMain.class
    jclass jclz = (*env)->GetObjectClass(env,jobj);

    //得FieldId, "count" 屬性名稱,"I"屬性簽名
    jfieldID fid = (*env)->GetStaticFieldID(env, jclz, "count", "I");

    //獲取count的值
    jint count = (*env)->GetStaticIntField(env,jclz,fid);

    count += 5;

    //給jobj的count靜態(tài)成員變量設(shè)置新的值
    (*env)->SetStaticIntField(env, jclz, fid, count);

}

最后編譯生成.dll動(dòng)態(tài)庫,并把動(dòng)態(tài)庫放到j(luò)ava工程里面。
接下來加載動(dòng)態(tài)庫并調(diào)用,代碼如下

public class JniMain {

    public static int count = 2;
    
    public native void accessStaticFieldModify();
    
    static{
        System.loadLibrary("JNI_Demo1");
    }
    
    public static void main(String[] args) {
        
        //調(diào)用非靜態(tài)native方法
        JniMain jm = new JniMain();
        
        System.out.println("change before count: " + count);
        jm.accessStaticFieldModify();
        System.out.println("after change count: " + count);
    }

}

最后的運(yùn)行結(jié)果:


以上就是兩個(gè)關(guān)于數(shù)據(jù)類型的舉例,相關(guān)代碼地址如下:
java工程:https://github.com/iwanttoseethecode/iava_jni_demo.git
c工程:https://github.com/iwanttoseethecode/c_jni_demo.git

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容