1. 程式人生 > >JNI/NDK開發指南(三)——JNI資料型別及與Java資料型別的對映關係

JNI/NDK開發指南(三)——JNI資料型別及與Java資料型別的對映關係

       當我們在呼叫一個Java native方法的時候,方法中的引數是如何傳遞給C/C++本地函式中的呢?Java方法中的引數與C/C++函式中的引數,它們之間是怎麼轉換的呢?我猜你應該也有相關的疑慮吧,咱們先來看一個例子,還是以HelloWorld為例:

HelloWorld.java:

package com.study.jnilearn;

class MyClass {}

public class HelloWorld {
	
	public static native void test(short s, int i, long l, float f, double d, char c, 
			boolean z, byte b, String str, Object obj, MyClass p, int[] arr);

	public static void main(String[] args) {
		String obj = "obj";
		short s = 1;
		long l = 20;
		byte b = 127;
		test(s, 1, l, 1.0f, 10.5, 'A', true, b, "中國", obj, new MyClass(), new int[] {});
	}
	
	static {
		System.loadLibrary("HelloWorld");
	}
}

在HelloWorld.java中定義了一個test的native方法,該方法中一個共有12個引數,其中前面8個為基本資料型別,後面4個全部為引用型別。

由HelloWorld.class生成的native函式原型及實現:

/*
 * Class:     com_study_jnilearn_HelloWorld
 * Method:    test
 * Signature: (SIJFDCZBLjava/lang/String;Ljava/lang/Object;Lcom/study/jnilearn/MyClass;[I)V
 */
JNIEXPORT void JNICALL Java_com_study_jnilearn_HelloWorld_test
    (JNIEnv *env, jclass cls, jshort s, jint i, jlong l, jfloat f,
     jdouble d, jchar c, jboolean z, jbyte b, jstring j_str, jobject jobj1, jobject job2, jintArray j_int_arr)
{
    printf("s=%hd, i=%d, l=%ld, f=%f, d=%lf, c=%c, z=%c, b=%d", s, i, l, f, d, c, z, b);
    const char *c_str = NULL;
    c_str = (*env)->GetStringUTFChars(env, j_str, NULL);
    if (c_str == NULL)
    {
        return; // memory out
    }
    (*env)->ReleaseStringUTFChars(env, j_str, c_str);
    printf("c_str: %s\n", (char*)c_str);
}

呼叫test方法的輸出結果:


從標頭檔案函式的原型可以得知,test方法中形參的資料型別自動轉換成了JNI中相應的資料型別,不難理解,在呼叫Java native方法將實參傳遞給C/C++函式的時候,會自動將java形參的資料型別自動轉換成C/C++相應的資料型別,所以我們在寫JNI程式的時候,必須要明白它們之間資料型別的對應關係。

在Java語言中資料型別分為基本資料型別和引用型別,其中基本資料型別有8種:byte、char、short、int、long、float、double、boolean,除了基本資料型別外其它都是引用型別:Object、String、陣列等。8種基本資料型別分別對應JNI資料型別中的jbyte、jchar、jshort、jint、jlong、jfloat、jdouble、jboolean。所有的JNI引用型別全部是jobject型別,為了使用方便和型別安全,JNI定義了一個引用型別集合,集合當中的所有型別都是jobject的子類,這些子類和Java中常用的引用型別相對應。例如:jstring表示字串、jclass表示class位元組碼物件、jthrowable表示異常、jarray表示陣列,另外jarray派生了8個子類,分別對應Java中的8種基本資料型別(jintArray、jshortArray、jlongArray等)。下面再回顧頭來看看test方法與Java_com_study_jnilearn_HelloWorld_test函式中引數型別的對應關係:

// HelloWorld.java
public static native void test(short s, int i, long l, float f, double d, char c, 
			boolean z, byte b, String str, Object obj, MyClass p);

// HelloWorld.h
JNIEXPORT void JNICALL Java_com_study_jnilearn_HelloWorld_test
  (JNIEnv *, jclass, jshort, jint, jlong, jfloat, jdouble, jchar, jboolean, jbyte, jstring, jobject, jobject, jintArray);

從上面兩個函式的引數中可以看出來,除了JNIEnv和jclass這兩個引數外,其它引數都是一一對應的。下面是JNI規範文件中描述Java與JNI資料型別的對應關係:

基本資料型別:


引用型別:


注意:

1、JNI如果使用C++語言編寫的話,所有引用型別派生自jobject,使用C++的繼承結構特性,使用相應的型別。如下所示:

class _jobject {};
   class _jclass : public _jobject {};
   class _jstring : public _jobject {};
   class _jarray : public _jobject {};
   class _jbooleanArray : public _jarray {};
   class _jbyteArray : public _jarray {};
   ...

2、JNI如果使用C語言編寫的話,所有引用型別使用jobject,其它引用型別使用typedef重新定義,如:typedef jobject jstring

jvalue型別:

jvalue是一個unio(聯合)型別,在C語中為了節約記憶體,會用聯合型別變數來儲存宣告在聯合體中的任意型別資料 。在JNI中將基本資料型別與引用型別定義在一個聯合型別中,表示用jvalue定義的變數,可以儲存任意JNI型別的資料,後面會介紹jvalue在JNI程式設計當中的應用。原型如下:

typedef union jvalue {
    jboolean z;
    jbyte    b;
    jchar    c;
    jshort   s;
    jint     i;
    jlong    j;
    jfloat   f;
    jdouble  d;
    jobject  l;
} jvalue;
如果對unio型別不太明白的同學,請參考相關資料,在這裡不細講。