Jni程式設計(二)jni.h 標頭檔案定義分析,以及c/c++呼叫java類的屬性和方法
在第一篇部落格中 我們初步瞭解了jni程式設計的步驟,那接下來我認為極其重要的事情是搞清楚jni.h標頭檔案裡面的結構,包括資料型別和方法的定義等,這些是必須的,否則沒有辦法進行學習,就像寫文章一樣,要先學會寫字是一樣的道理。
首先來看一下jni.h標頭檔案的組成:ps下面這張圖是盜來的,我覺得這張圖挺好的,莫怪莫怪,哈哈
下面我們就開啟jni.h(位於jdk安裝目錄下的include資料夾下面)原始碼來分析一下這個結構
一、jni規範中定義的資料型別.
a> jni中的基本資料型別 對應的Java ,c/c++,jni 的資料型別參照如下:
Java型別 | 本地型別 | JNI定義的別名 | 位元組數 |
int | long | jint/jsize | C/C++帶符號的32位整型 |
long | _int64 | jlong | C/C++帶符號的64位整型 |
byte | signed char | jbyte | C/C++帶符號的8位整型 |
boolean | unsigned char | jboolean | C/C++8位整型 |
char | unsigned short | jchar | C/C++無符號的16位整型 |
short | shot | jshort | C/C++帶符號的16位整型 |
float | float | jfloat | C/C++32位浮點型 |
double | double | jdouble | C/C++64位浮點型 |
Object | _jobject* | jobject |
在jni.h中原始碼定義如下:
/* * JNI Types */ #ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H typedef unsigned char jboolean; typedef unsigned short jchar; typedef short jshort; typedef float jfloat; typedef double jdouble; typedef jint jsize;
b> 引用型別
java型別 | native型別 | 描述 |
---|---|---|
Object | jobject | 任何物件 |
Class | jclass | Class類物件 |
String | jstring | 字串 |
Object[] | jobjectArray | 任何物件的陣列 |
int[] | jintArray | int陣列 |
char[] | jcharArray | 字元型物件 |
... | ... | ... |
二、jni中陣列的定義
c++中所有的類都是繼承至_jobject 類 是以類來定義的,c語言中是以結構體定義的如下:#else 下面的是對應的c語言對陣列的定義,上面是c++對陣列的定義
#ifdef __cplusplus
class _jobject {};
class _jclass : public _jobject {};
class _jthrowable : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jobjectArray : public _jarray {};
typedef _jobject *jobject;
typedef _jclass *jclass;
typedef _jthrowable *jthrowable;
typedef _jstring *jstring;
typedef _jarray *jarray;
typedef _jbooleanArray *jbooleanArray;
typedef _jbyteArray *jbyteArray;
typedef _jcharArray *jcharArray;
typedef _jshortArray *jshortArray;
typedef _jintArray *jintArray;
typedef _jlongArray *jlongArray;
typedef _jfloatArray *jfloatArray;
typedef _jdoubleArray *jdoubleArray;
typedef _jobjectArray *jobjectArray;
#else
struct _jobject;
typedef struct _jobject *jobject;
typedef jobject jclass;
typedef jobject jthrowable;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;
#endif
三、方法簽名時用到的公共體型別
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;
四、屬性id的定義(獲取Java類的屬性)
struct _jfieldID;
typedef struct _jfieldID *jfieldID;
五、方法id的定義
struct _jmethodID;
typedef struct _jmethodID *jmethodID;
六、幾種不同型別引用的定義
/* Return values from jobjectRefType */
typedef enum _jobjectType {
JNIInvalidRefType = 0,
JNILocalRefType = 1,
JNIGlobalRefType = 2,
JNIWeakGlobalRefType = 3
} jobjectRefType;
七、關於錯誤型別的定義
/*
* possible return values for JNI functions.
*/
#define JNI_OK 0 /* success */
#define JNI_ERR (-1) /* unknown error */
#define JNI_EDETACHED (-2) /* thread detached from the VM */
#define JNI_EVERSION (-3) /* JNI version error */
#define JNI_ENOMEM (-4) /* not enough memory */
#define JNI_EEXIST (-5) /* VM already created */
#define JNI_EINVAL (-6) /* invalid arguments */
八、釋放陣列是否關心拷貝的定義
/*
* used in ReleaseScalarArrayElements
*/
#define JNI_COMMIT 1
#define JNI_ABORT 2
九、JNI Native Method Interface.(java本地方法介面定義) 即 JNIEnv相關定義
/*
* JNI Native Method Interface.
*/
struct JNINativeInterface_;
struct JNIEnv_;
#ifdef __cplusplus
typedef JNIEnv_ JNIEnv;
#else
typedef const struct JNINativeInterface_ *JNIEnv;
#endif
十、java虛擬機器介面
/*
* JNI Invocation Interface.
*/
struct JNIInvokeInterface_;
struct JavaVM_;
#ifdef __cplusplus
typedef JavaVM_ JavaVM;
#else
typedef const struct JNIInvokeInterface_ *JavaVM;
#endif
以上就是jni.h標頭檔案中 幾大塊的定義。
那麼JNIEnv,jobject,jclass,從原始碼中看到了這幾塊的定義,在第一篇的部落格中也簡單分析了這三個含義,但是我覺得還不夠。
JNIEnv 分析:
C++中:JNIEnv就是struct _JNIEnv。 JNIEnv* env等價於struct _JNIEnv*env,在呼叫JNI函式的時候,只需要env-> FindClass(JNIEnv*, const char*),就會間接呼叫JNINativeInterface結構體裡定義的函式指標,而無需首先對env解引用。
C中:JNIEnv就是const struct JNINativeInterface*。JNIEnv* env實際等價於const struct JNINativeInterface** env,因此要得到JNINativeInterface結構體內的函式指標就必須先對env解引用得到(*env),即const struct JNINativeInterface*,這個指標才是真正指向JNINativeInterface結構體的指標,然後再通過它呼叫具體的JNI函式。因此需要這樣呼叫:(*env)-> FindClass(JNIEnv*, const char*)。
注意: JNIEnv只在當前執行緒中有效。本地方法不能將JNIEnv從一個執行緒傳遞到另一個執行緒中。相同的 Java 執行緒中對本地方法多次呼叫時,傳遞給該本地方法的JNIEnv是相同的。但是,一個本地方法可被不同的 Java 執行緒所呼叫,因此可以接受不同的 JNIEnv。
看一下JNINativeInterface_裡面是什麼:(擷取部分原始碼)
struct JNINativeInterface_ {
void *reserved0;
void *reserved1;
void *reserved2;
void *reserved3;
jint (JNICALL *GetVersion)(JNIEnv *env);
jclass (JNICALL *GetObjectClass)
(JNIEnv *env, jobject obj);
jboolean (JNICALL *IsInstanceOf)
(JNIEnv *env, jobject obj, jclass clazz);
jmethodID (JNICALL *GetMethodID)
(JNIEnv *env, jclass clazz, const char *name, const char *sig);
jobject (JNICALL *CallObjectMethod)
(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jobject (JNICALL *CallObjectMethodV)
(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jboolean (JNICALL *CallBooleanMethod)
(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jboolean (JNICALL *CallBooleanMethodV)
(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jbyte (JNICALL *CallByteMethod)
(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jbyte (JNICALL *CallByteMethodV)
(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
jchar (JNICALL *CallCharMethod)
(JNIEnv *env, jobject obj, jmethodID methodID, ...);
jchar (JNICALL *CallCharMethodV)
(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
.......
void (JNICALL *CallVoidMethod)
(JNIEnv *env, jobject obj, jmethodID methodID, ...);
void (JNICALL *CallVoidMethodV)
(JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
void (JNICALL *CallVoidMethodA)
(JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args);
......
jobjectArray (JNICALL *NewObjectArray)
(JNIEnv *env, jsize len, jclass clazz, jobject init);
jobject (JNICALL *GetObjectArrayElement)
(JNIEnv *env, jobjectArray array, jsize index);
void (JNICALL *SetObjectArrayElement)
(JNIEnv *env, jobjectArray array, jsize index, jobject val);
jbooleanArray (JNICALL *NewBooleanArray)
(JNIEnv *env, jsize len);
jbyteArray (JNICALL *NewByteArray)
(JNIEnv *env, jsize len);
jcharArray (JNICALL *NewCharArray)
(JNIEnv *env, jsize len);
jshortArray (JNICALL *NewShortArray)
jboolean * (JNICALL *GetBooleanArrayElements)
(JNIEnv *env, jbooleanArray array, jboolean *isCopy);
jbyte * (JNICALL *GetByteArrayElements)
(JNIEnv *env, jbyteArray array, jboolean *isCopy);
jchar * (JNICALL *GetCharArrayElements)
(JNIEnv *env, jcharArray array, jboolean *isCopy);
...........
void (JNICALL *ReleaseByteArrayElements)
(JNIEnv *env, jbyteArray array, jbyte *elems, jint mode);
..........
};
看一下JNIEnv_裡面是什麼:(擷取部分原始碼)
struct JNIEnv_ {
const struct JNINativeInterface_ *functions;
#ifdef __cplusplus
jint GetVersion() {
return functions->GetVersion(this);
}
jclass DefineClass(const char *name, jobject loader, const jbyte *buf,
jsize len) {
return functions->DefineClass(this, name, loader, buf, len);
}
jclass FindClass(const char *name) {
return functions->FindClass(this, name);
}
........................
jobject NewObject(jclass clazz, jmethodID methodID, ...) {
va_list args;
jobject result;
va_start(args, methodID);
result = functions->NewObjectV(this,clazz,methodID,args);
va_end(args);
return result;
}
jclass GetObjectClass(jobject obj) {
return functions->GetObjectClass(this,obj);
}
jboolean IsInstanceOf(jobject obj, jclass clazz) {
return functions->IsInstanceOf(this,obj,clazz);
}
jmethodID GetMethodID(jclass clazz, const char *name,
const char *sig) {
return functions->GetMethodID(this,clazz,name,sig);
}
jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) {
va_list args;
jobject result;
va_start(args,methodID);
result = functions->CallObjectMethodV(this,obj,methodID,args);
va_end(args);
return result;
}
.....................................................
jobject GetObjectField(jobject obj, jfieldID fieldID) {
return functions->GetObjectField(this,obj,fieldID);
}
jboolean GetBooleanField(jobject obj, jfieldID fieldID) {
return functions->GetBooleanField(this,obj,fieldID);
}
jbyte GetByteField(jobject obj, jfieldID fieldID) {
return functions->GetByteField(this,obj,fieldID);
}
void SetObjectField(jobject obj, jfieldID fieldID, jobject val) {
functions->SetObjectField(this,obj,fieldID,val);
}
......................................
jchar CallStaticCharMethod(jclass clazz,
jmethodID methodID, ...) {
va_list args;
jchar result;
va_start(args,methodID);
result = functions->CallStaticCharMethodV(this,clazz,methodID,args);
va_end(args);
return result;
}
jstring NewString(const jchar *unicode, jsize len) {
return functions->NewString(this,unicode,len);
}
jsize GetStringLength(jstring str) {
return functions->GetStringLength(this,str);
}
................................
jobjectArray NewObjectArray(jsize len, jclass clazz,
jobject init) {
return functions->NewObjectArray(this,len,clazz,init);
}
jobject GetObjectArrayElement(jobjectArray array, jsize index) {
return functions->GetObjectArrayElement(this,array,index);
}
void SetObjectArrayElement(jobjectArray array, jsize index,
jobject val) {
functions->SetObjectArrayElement(this,array,index,val);
}
...............................................
void ReleaseByteArrayElements(jbyteArray array,
jbyte *elems,
jint mode) {
functions->ReleaseByteArrayElements(this,array,elems,mode);
}
void ReleaseCharArrayElements(jcharArray array,
jchar *elems,
jint mode) {
functions->ReleaseCharArrayElements(this,array,elems,mode);
}
void ReleaseShortArrayElements(jshortArray array,
jshort *elems,
jint mode) {
functions->ReleaseShortArrayElements(this,array,elems,mode);
}
void ReleaseIntArrayElements(jintArray array,
jint *elems,
jint mode) {
functions->ReleaseIntArrayElements(this,array,elems,mode);
}
void ReleaseLongArrayElements(jlongArray array,
jlong *elems,
jint mode) {
functions->ReleaseLongArrayElements(this,array,elems,mode);
}
void ReleaseFloatArrayElements(jfloatArray array,
jfloat *elems,
jint mode) {
functions->ReleaseFloatArrayElements(this,array,elems,mode);
}
void ReleaseDoubleArrayElements(jdoubleArray array,
jdouble *elems,
jint mode) {
functions->ReleaseDoubleArrayElements(this,array,elems,mode);
}
...............
jobjectRefType GetObjectRefType(jobject obj) {
return functions->GetObjectRefType(this, obj);
}
#endif /* __cplusplus */
};
看了原始碼就終於明白了,為什麼我們可以用JNIEnv來呼叫很多api方法了,以及c和c++呼叫方法的時候為什麼寫法不同。
jobject,與jclass 分析:
jobject第一篇部落格提到了,jobject 就代表了 java中包含native方法的類的一個例項,準確來說就是呼叫本地方法的那個例項。
jclass 代表的是一個類物件,而不是一個類的例項。如下圖:盜來的,哈哈。。。。
第一個方法是對應Java類中的非靜態 native方法,對應的第二個引數是jboject
第二個方法是對應的Java類中的靜態(static修飾)native方法,第二個引數對應的是jclass.
why???????
因為,靜態方法與靜態變數一樣,屬於類本身,而不屬於那個類的一個物件。呼叫一個被定義為static的方法,可以通過在它前面加上這個類的名稱,也可以像呼叫非靜態方法一樣通過類物件呼叫。
例項方法必須通過類的例項來使用。例項方法可以使用類的非靜態成員,也可以使用類的靜態成員。
類的靜態方法,靜態變數是在類裝載的時候裝載的。但是要特別注意,類的靜態變數是該類的物件所共有的,即是所有物件共享變數。所以建議儘量少用靜態變數。儘量在靜態方法中使用內部變數。
在C/C++中呼叫 Java類中的屬性和方法
繼續使用上一篇部落格的例子
java端程式碼:和上一篇相比多定義了一個 num 屬性和 getMax方法
package com.koimy;
public class Main {
int num = 123;
static {
System.loadLibrary("NativeCode");
}
public native void sayHello();
public int getMax(int num1, int num2) {
return num1 > num2 ? num1 : num2;
}
public static void main(String[] args) {
Main main = new Main();
main.sayHello();
System.out.println("Java: 這是Java輸出得num = " + main.num);
}
}
C++端程式碼:獲取Java端 main這個物件的的num 屬性的值輸出到控制檯,並修改他的值,同時呼叫getMax方法
#include<iostream>
#include"com_koimy_Main.h"
using namespace std;
JNIEXPORT void JNICALL Java_com_koimy_Main_sayHello (JNIEnv *env, jobject obj)
{
// cout<<"This message is from JNI by C++!"<<endl;
jclass class_nativecode = env->GetObjectClass(obj);
//===========================c/c++呼叫Java類得屬性==========================================
jfieldID id_num = env->GetFieldID(class_nativecode,"num","I");
jint num = env->GetIntField(obj,id_num);
cout<<"C++: num = "<<num<<endl;
env->SetIntField(obj,id_num,666L);
//===========================c/c++呼叫Java類的方法==========================================
jmethodID id_getmax = env->GetMethodID(class_nativecode,"getMax","(II)I");
jint max = env->CallIntMethod(obj,id_getmax,100L,36L);
cout<<"C++: max = "<<max<<endl;
}
最後控制檯輸出如下:
呼叫屬性和方法的步驟:
1.獲得jclass
2.獲得 jfiledID/ jmethodID
3.通過env呼叫 get<TYPE>Field() 或者呼叫Call<Type>Method();
OK就這麼多吧!
PS:歡迎加入我的qq群一起學習:239074811