接續(xù)上篇JNI開發(fā)系列③C語言調(diào)用Java字段與方法
前情提要
Java調(diào)用C方法很簡單 , 只需要編寫native方法即可 , 通過C去調(diào)用Java的字段與方法 , 則需要比較復(fù)雜的操作 , 上篇中介紹了 , C調(diào)用的Java字段與方法的幾個(gè)套路:
步驟一 、 得到j(luò)class, 字節(jié)碼對象 , 如果是static native修飾 , 則函數(shù)會以jclass類型傳入 , 非靜態(tài)則需要得到j(luò)class類型 。
步驟二 、得到字段或方法ID , 區(qū)分靜態(tài)字段與對象字段 , 靜態(tài)字段或方法調(diào)用(env)->GetStaticFieldID,(env)->GetMethodID函數(shù)得到ID , 對象字段調(diào)用(env)->GetFieldID,(env)->GetStaticMethodID得到ID 。 可以得到一個(gè)套路 , 靜態(tài)修飾的 , 則調(diào)用static標(biāo)識的函數(shù) , 非靜態(tài)的則調(diào)用常規(guī)函數(shù) 。
步驟三 、 取得字段的值或調(diào)用方法 , 需要注意的是, 得到字段的值與調(diào)用方法 , 都有類型的區(qū)分 。引用類型則使用GetObjectField, CallStaticObjectMethod, 其他類型 , 則有對于的jxxx類型對應(yīng) 。套路簡寫:Get<Type>Field, GetStatic<Type>Field, Call<Type>Method, CallStatic<Type>Method 。
步驟四 、 類型轉(zhuǎn)換 , 如果是Java引用類型 , 則需要進(jìn)行類型轉(zhuǎn)換
C 調(diào)用Java對象的構(gòu)造方法
C調(diào)用Java的構(gòu)造方法與調(diào)用普通方法略有不同 , 其不同之處在于方法名稱上 , 普通方法直接使用
方法名, 構(gòu)造方法則不是使用類名 , 而使用一個(gè)固定寫法<init>。
java code 使用C語言創(chuàng)建一個(gè)Date對象,獲取時(shí)間戳
private native long accessConstructorMethod() ;
public static void main(String[] args) {
long timeLong = jni.accessConstructorMethod() ;
SimpleDateFormat sdf = new SimpleDateFormat();
sdf.applyPattern("yyyy-MM-dd HH:mm:ss") ;
String dateString = sdf.format(new Date(timeLong)) ;
}
使用C創(chuàng)建Java對象 , 并調(diào)用方法
/*訪問java對象的構(gòu)造方法*/
JNIEXPORT jlong JNICALL Java_com_zeno_jni_HelloJni_accessConstructorMethod
(JNIEnv *env, jobject jobj) {
// 找到Date的jclass
jclass dateCls = (*env)->FindClass(env, "java/util/Date");
// 得到構(gòu)造方法id
jmethodID dateConstructMid = (*env)->GetMethodID(env, dateCls, "<init>", "()V");
// 創(chuàng)建Date對象
jobject dateObj = (*env)->NewObject(env, dateCls, dateConstructMid);
// 得到getTime方法ID
jmethodID getTimeMid = (*env)->GetMethodID(env, dateCls, "getTime", "()J");
// 調(diào)用getTime方法
jlong timeLong = (*env)->CallLongMethod(env, dateObj, getTimeMid);
printf("new Date().getTime : %lld\n", timeLong);
return timeLong;
}
Tips:
C實(shí)例化Java對象的時(shí)候 , 首先需要找到對象的class , 以全類名表示 , .用/代替 (java.util.Date --> Java/util/Date)。Java的構(gòu)造函數(shù)名稱為固定的<init>名稱 , 通過NewObject()函數(shù)來創(chuàng)建Java對象 。需要注意的是簽名 , 我們可以通過javap -s -p來獲取簽名 。常見簽名如下:
| 數(shù)據(jù)類型 | 簽名 |
|---|---|
| boolean | Z |
| byte | B |
| char | C |
| short | S |
| int | I |
| long | L |
| float | F |
| double | D |
| void | V |
| object | L開頭,然后以/分隔的完整類型,后面再加;例如:String類型簽名 Ljava/lang/String
|
| Array | 以[開頭,再加上數(shù)組元素的簽名。例如int[]的簽名是 [I , int[][]是[[I |
C與Java的字符轉(zhuǎn)換
在開發(fā)中 , 我們常常都會遇到字符編碼問題 , web開發(fā)的時(shí)候 , 時(shí)常需要指定網(wǎng)頁的編碼 , 國內(nèi)一般常用GBK與UTF-8 ,這兩種編碼格式 。 我們在開發(fā)項(xiàng)目的時(shí)候 , 也會指定項(xiàng)目的文件編碼 , 因?yàn)椴煌募幋a對于漢字的支持與處理不同 , GBK采用的是一字節(jié)和雙字節(jié)編碼 , 而UTF-8則采用的是 , 一至四個(gè)字節(jié)編碼 , 單個(gè)字符使用一個(gè)直接表示 , 漢字則使用四個(gè)字節(jié)表示 。
java code
private native String cTransformChar(String str) ;
public static void main(String[] args) {
HelloJNI jni = new HelloJNI() ;
System.out.println(jni.cTransformChar("住"));
}
C處理字符編碼
/*C的字符轉(zhuǎn)換*/
JNIEXPORT jstring JNICALL Java_com_zeno_jni_HelloJNI_cTransformChar
(JNIEnv *env, jobject jobj,jstring jStr) {
// 將jstring轉(zhuǎn)換成c字符指針
char *cStr = (*env)->GetStringUTFChars(env, jStr, NULL);
jsize js = (*env)->GetStringLength(env, jStr);
printf("傳進(jìn)來的值size : %d\n", js);
printf("傳進(jìn)來的值 : %s\n", cStr);
char destBuffer[50] = "非我 and 小九";
// 得到String類
jclass stringCls = (*env)->FindClass(env, "java/lang/String");
// 得到構(gòu)造方法的ID
jmethodID stringConstructMid = (*env)->GetMethodID(env, stringCls, "<init>", "([BLjava/lang/String;)V");
/*
使用到的Java構(gòu)造方法
String(byte[] bytes, String charsetName)
通過使用指定的 charset 解碼指定的 byte 數(shù)組,構(gòu)造一個(gè)新的 String。
*/
// 構(gòu)建一個(gè)bytes數(shù)組
jbyteArray strBytes = (*env)->NewByteArray(env, 50);
// 設(shè)置字符數(shù)組
(*env)->SetByteArrayRegion(env, strBytes, 0, strlen(destBuffer), destBuffer);
// 構(gòu)建字符編碼
jstring charSetName = (*env)->NewStringUTF(env, "GBK");
// 創(chuàng)建String類的對象
jstring transformStr = (*env)->NewObject(env, stringCls, stringConstructMid, strBytes, charSetName);
return transformStr;
}
Tips
如果是使用的eclipse控制臺輸出 , 一定要注意控制臺的字符編碼 , 如果編碼不統(tǒng)一 , 控制臺就會輸出亂碼 ,如:如果要輸出的字符是UTF-8而控制臺是GB2312 , 則會輸出亂碼 。 如果出現(xiàn)亂碼 , 可以將字符寫入到文件中 , 看看是否會亂碼 ,如果亂碼 ,可以使用notepad++看一下文件的編碼 , 如果編碼一致 , 則會顯示正常 。
結(jié)語
這幾天公司產(chǎn)品上線 , 有點(diǎn)忙碌 , 沒多少時(shí)間寫文章 , 今天恢復(fù)寫文章的進(jìn)度。昨天和一個(gè)創(chuàng)業(yè)公司的CEO聊了聊 , 發(fā)現(xiàn)自己欠缺的東西還有很多很多 , 對于產(chǎn)品的架構(gòu) , 如何從0到1 , 將想法轉(zhuǎn)換為產(chǎn)品 , 沒有一個(gè)完整的產(chǎn)品思路 ,也沒有產(chǎn)品經(jīng)理的思維能力,有的只是對界面的吹毛求疵 , 對部分功能的自以為是 。任何行業(yè) , 沒有深入進(jìn)去 , 看的永遠(yuǎn)只是表面 , 都是別人玩剩下的 。