Java類型和本地類型對應
在如下情況下,需要在本地方法中應用java對象的引用,就會用到類型之間的轉換:
- java方法里面將參數傳入本地方法;
- 在本地方法里面創建java對象;
- 在本地方法里面return結果給java程序。
Java基本類型
像booleans、integers、floats等從Java程序中傳到本地方法中的原始類型可以直接使用,下面是java中的原始類型和本地方法中的類型的對應:
Java 類型本地類型說明
boolean jboolean 無符號,8 位
byte jbyte 無符號,8 位
char jchar 無符號,16 位
short jshort 有符號,16 位
int jint 有符號,32 位
long jlong 有符號,64 位
float jfloat 32 位
double jdouble 64 位
void void N/A
也就是說如果我在方法中傳進去了一個boolean的參數的話,那么我在本地方法中就有jboolean類型與之對應。同理,如果在本地方法中return一個jint的話,那么在java中就返回一個int類型。
為了使用方便,特提供以下定義。
#define JNI_FALSE 0
#define JNI_TRUE 1
jsize 整數類型用于描述主要指數和大小:
typedef jint jsize;
Java String類型
在java中,使用的字符串String對象是Unicode碼,即每個字符不論是中文還是英文或是符號,一個字符總是占用兩個字節。
在c/c++本地代碼中創建java的String對象
.java通過JNI接口可以將java的字符串轉換到c/c++中的寬字符串(wchar_t ),或是傳回一個UTF-8的字符串(char )到c/c++。反過來,c/c++可以通過一個寬字符串,或是一個UTF-8編碼的字符串來創建一個java端的String對象。
GetStringChars/GetStringUTFChars
.這兩個函數用來取得與某個jstring對象相關的java字符串。分別可以取得UTF-16編碼的寬字符串(jchar)跟UTF-8編碼的字符串(char)。
Const jchar* GetStringChars(jstring str, jboolean* copied)
Const char* GetStringUTFChars(jstring str, jboolean* copied)
第一個參數傳入一個指向java中的String對象的jstring變量
第二個參數傳入的是一個jboolean的指針。
這兩個函數分別都會有兩個不同的動作:
第一個參數:
- 開新內存,然后把java中的String拷貝到這個內存中,然后返回這個內存地址的指針。
直接返回指向java中string的內存的指針,這個時候千萬不要改變這個內存的內容,這將破壞String在java中始終是常量這個原則。
第二個參數:
是用來標示是否對java的string對象進行了拷貝的。
如果傳入的這個jboolean指針不是null,則他會給該指針指向的內存傳入JNI_TRUE或JNI_FALSE標示是否進行了拷貝。
傳入null標示不關心是否拷貝字符串,它就不會給jboolean*指向的內存賦值。
使用這兩個函數取得的字符串,在不使用的時候,要使用ReleaseStringChars/ReleaseStringUTFChars
來釋放拷貝的內存,或是釋放對java的String對象的引用。ReleaseStringChars(jstring jstr, const jchar* str);
ReleaseStringUTFChars(jstring jstr, const char* str);
第一個參數指定一個jstring變量,即是要釋放的本地字符串的來源。
第二個參數就是要釋放的本地字符串
訪問類對象的屬性
env 為 JNIEnv,obj的類型為jobject
JAVA_FieldAccessTest_accessField(JNIEnv *env,jobject obj){jfieldID fid;jclass cls = (*env)->GetObjectClass(env, obj);//類FieldAccessTest中有個String類型的屬性s//獲取要訪問的屬性的idfid = (*env)->GetFieldID(evn,cls,"s","Ljava/lang/String;");//讀取屬性值jstring jstr = (*env)->GetObjectField(env,obj,fid);char* str = (*evn)->GetStringUTFChars(env,jstr,NULL);//釋放資源
(*env)->ReleaseStringUTFChars(env,jstr,str);//現在反過來,改變調用該本地方法的java對象的屬性值jstr = (*env)->NewStringUTF(env,"88888");(*env)->SetObjectField(env,obj,fid,jstr);}
總結:
1. jfieldID fid = (*env)->GetFieldID(env,對象所屬的類的jclass,屬性名,屬性對應的屬性描述符號);
2. (*env)->GetObjectField(env,對象,屬性id);
訪問靜態屬性:
假如有個類如下:
class StaticFielcTest {private static int si;private native void accessField();
}
那么實現為:
JNIEXPORT void JNICALL
Java_StaticFieldTest_accessField(JNIEnv *env, jobject obj)
{jfieldID fid; /* store the field ID */jint si;jclass cls = (*env)->GetObjectClass(env, obj); //獲取類classfid = (*env)->GetStaticFieldID(env, cls, "si", "I"); //獲取靜態屬性idsi = (*env)->GetStaticIntField(env, cls, fid); //讀去屬性的值(*env)->SetStaticIntField(env, cls, fid, 200); //設置靜態屬性的值
}
訪問實例方法
假如有個這樣的類:
class MethodCall {private native void nativeMethod();private void callback() {System.out.println("In Java CallBack");}public static void main(String args[]) {MethodCall c = new MethodCall();c.nativeMethod();
} static {System.loadLibrary("InstanceMethodCall");}
}
jni實現:
JNIEXPORT void JNICALL
Java_MethodCall_nativeMethod(JNIEnv *env, jobject obj)
{//1.拿到classjclass cls = (*env)->GetObjectClass(env, obj); //2.拿到方法idjmethodID mid = (*env)->GetMethodID(env, cls, "callback", "()V"); //3.根據obj,和方法id 調用方法(*env)->CallVoidMethod(env, obj, mid);
}
根據方法的返回值來決定調用哪個方法:
如果返回int 那么最后一步就調用 (*env)->CallIntMethod(env,obj,mid)
;
最后那個參數 "()V" 是方法描述符:
(I)V 帶一個int 類型的參數,返回值類型為void
()D 沒有參數,返回double //注意!!沒有參數并不是 (V)D
方法public static void main(String[] args) 對應的方法描的符為:
([Ljava/lang/String;)V
訪問靜態方法
jclass cls = (*env)->GetObjectClass(env, obj);
jmethodID mid =(*env)->GetStaticMethodID(env, cls, "callback", "()V");
(*env)->CallStaticVoidMethod(env, cls, mid); //注意,這里跟訪問實例方法的區別是 第二個參數不是obj,而是cls