接續上篇[JNI開發系列③C語言調用Java字段與方法](http://www.jianshu.com/p/9cc6e3f0ead7)
###前情提要
Java調用C方法很簡單 , 只需要編寫`native`方法即可 , 通過C去調用Java的字段與方法 , 則需要比較復雜的操作 , 上篇中介紹了 , C調用的Java字段與方法的幾個套路:
> 步驟一 、 得到jclass, 字節碼對象 , 如果是static native修飾 , 則函數會以jclass類型傳入 , 非靜態則需要得到jclass類型 。
>步驟二 、得到字段或方法ID , 區分靜態字段與對象字段 , 靜態字段或方法調用(*env)->GetStaticFieldID,(*env)->GetMethodID函數得到ID , 對象字段調用(*env)->GetFieldID,(*env)->GetStaticMethodID得到ID 。 可以得到一個套路 , 靜態修飾的 , 則調用static標識的函數 , 非靜態的則調用常規函數 。
> 步驟三 、 取得字段的值或調用方法 , 需要注意的是, 得到字段的值與調用方法 , 都有類型的區分 。引用類型則使用GetObjectField,?CallStaticObjectMethod, 其他類型 , 則有對于的jxxx類型對應 。套路簡寫:Get<Type>Field,?GetStatic<Type>Field,?Call<Type>Method,?CallStatic<Type>Method?。
> 步驟四 、 類型轉換 , 如果是Java引用類型 , 則需要進行類型轉換
### C 調用Java對象的構造方法
> C調用Java的構造方法與調用普通方法略有不同 , 其不同之處在于方法名稱上 , 普通方法直接使用`方法名` , 構造方法則不是使用類名 , 而使用一個固定寫法`<init>` 。
```java
java code 使用C語言創建一個Date對象,獲取時間戳
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創建Java對象 , 并調用方法
```c
/*訪問java對象的構造方法*/
JNIEXPORT jlong JNICALL Java_com_zeno_jni_HelloJni_accessConstructorMethod
(JNIEnv *env, jobject jobj) {
// 找到Date的jclass
jclass dateCls = (*env)->FindClass(env, "java/util/Date");
// 得到構造方法id
jmethodID dateConstructMid = (*env)->GetMethodID(env, dateCls, "<init>", "()V");
// 創建Date對象
jobject dateObj = (*env)->NewObject(env, dateCls, dateConstructMid);
// 得到getTime方法ID
jmethodID getTimeMid = (*env)->GetMethodID(env, dateCls, "getTime", "()J");
// 調用getTime方法
jlong timeLong = (*env)->CallLongMethod(env, dateObj, getTimeMid);
printf("new Date().getTime : %lld\n", timeLong);
return timeLong;
}
```
_Tips:_
C實例化Java對象的時候 , 首先需要找到對象的class , 以全類名表示 , `.`用`/`代替 (`java.util.Date --> Java/util/Date`)。Java的構造函數名稱為固定的`<init>`名稱 , 通過`NewObject()`函數來創建Java對象 。需要注意的是簽名 , 我們可以通過`javap -s -p`來獲取簽名 。常見簽名如下:
|數據類型|簽名|
|:---:|:---:|
|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|以[開頭,再加上數組元素的簽名。例如int[]的簽名是 [I , int[][]是[[I|
### C與Java的字符轉換
在開發中 , 我們常常都會遇到字符編碼問題 , web開發的時候 , 時常需要指定網頁的編碼 , 國內一般常用GBK與UTF-8 ,這兩種編碼格式 。 我們在開發項目的時候 , 也會指定項目的文件編碼 , 因為不同文件編碼對于漢字的支持與處理不同 , GBK采用的是一字節和雙字節編碼 , 而UTF-8則采用的是 , 一至四個字節編碼 , 單個字符使用一個直接表示 , 漢字則使用四個字節表示 。
```java
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
/*C的字符轉換*/
JNIEXPORT jstring JNICALL Java_com_zeno_jni_HelloJNI_cTransformChar
(JNIEnv *env, jobject jobj,jstring jStr) {
// 將jstring轉換成c字符指針
char *cStr = (*env)->GetStringUTFChars(env, jStr, NULL);
jsize js = (*env)->GetStringLength(env, jStr);
printf("傳進來的值size : %d\n", js);
printf("傳進來的值 : %s\n", cStr);
char destBuffer[50] = "非我 and 小九";
// 得到String類
jclass stringCls = (*env)->FindClass(env, "java/lang/String");
// 得到構造方法的ID
jmethodID stringConstructMid = (*env)->GetMethodID(env, stringCls, "<init>", "([BLjava/lang/String;)V");
/*
使用到的Java構造方法
String(byte[] bytes, String charsetName)
通過使用指定的 charset 解碼指定的 byte 數組,構造一個新的 String。
*/
// 構建一個bytes數組
jbyteArray strBytes = (*env)->NewByteArray(env, 50);
// 設置字符數組
(*env)->SetByteArrayRegion(env, strBytes, 0, strlen(destBuffer), destBuffer);
// 構建字符編碼
jstring charSetName = (*env)->NewStringUTF(env, "GBK");
// 創建String類的對象
jstring transformStr = (*env)->NewObject(env, stringCls, stringConstructMid, strBytes, charSetName);
return transformStr;
}
```
_Tips_
如果是使用的eclipse控制臺輸出 , 一定要注意控制臺的字符編碼 , 如果編碼不統一 , 控制臺就會輸出亂碼 ,如:如果要輸出的字符是UTF-8而控制臺是GB2312 , 則會輸出亂碼 。 如果出現亂碼 , 可以將字符寫入到文件中 , 看看是否會亂碼 ,如果亂碼 ,可以使用notepad++看一下文件的編碼 , 如果編碼一致 , 則會顯示正常 。
### 結語
這幾天公司產品上線 , 有點忙碌 , 沒多少時間寫文章 , 今天恢復寫文章的進度。昨天和一個創業公司的CEO聊了聊 , 發現自己欠缺的東西還有很多很多 , 對于產品的架構 , 如何從0到1 , 將想法轉換為產品 , 沒有一個完整的產品思路 ,也沒有產品經理的思維能力,有的只是對界面的吹毛求疵 , 對部分功能的自以為是 。任何行業 , 沒有深入進去 , 看的永遠只是表面 , 都是別人玩剩下的 。
### 刻意練習技術 , 時刻思考產品 。
- 簡介
- C語言基礎及指針①語法基礎
- C語言基礎及指針②之指針內存分析
- C語言基礎及指針③函數與二級指針
- C語言基礎及指針④函數指針
- C語言基礎及指針⑤動態內存分配
- C語言基礎及指針⑥字符操作
- C語言基礎及指針⑦結構體與指針
- C語言基礎及指針⑧文件IO
- C語言基礎及指針⑨聯合體與枚舉
- C語言基礎及指針⑩預編譯及jni.h分析
- JNI開發系列①JNI概念及開發流程
- JNI開發系列②.h頭文件分析
- JNI開發系列③C語言調用Java字段與方法
- JNI開發系列④C語言調用構造方法
- JNI開發系列⑤對象引用的處理
- NDK開發基礎①使用Android Studio編寫NDK
- NDK開發基礎②文件加密解密與分割合并
- NDK開發基礎③增量更新之服務器端生成差分包
- C++基礎①命名空間結構體和引用