1. 程式人生 > >Jni程式設計(二)jni.h 標頭檔案定義分析,以及c/c++呼叫java類的屬性和方法

Jni程式設計(二)jni.h 標頭檔案定義分析,以及c/c++呼叫java類的屬性和方法

第一篇部落格中 我們初步瞭解了jni程式設計的步驟,那接下來我認為極其重要的事情是搞清楚jni.h標頭檔案裡面的結構,包括資料型別和方法的定義等,這些是必須的,否則沒有辦法進行學習,就像寫文章一樣,要先學會寫字是一樣的道理。

首先來看一下jni.h標頭檔案的組成:ps下面這張圖是盜來的,我覺得這張圖挺好的,莫怪莫怪,哈哈

下面我們就開啟jni.h(位於jdk安裝目錄下的include資料夾下面)原始碼來分析一下這個結構

一、jni規範中定義的資料型別.

   a> jni中的基本資料型別 對應的Java ,c/c++,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