JNI 方法註冊與簽名+BufferedReader使用readLine問題
阿新 • • 發佈:2017-06-10
ica process 知識 start tab .net lib 構造 edr
自動生成對應的c層頭文件 下面是靜態註冊的例子: Java層: [java] view plain copy
native層: [cpp] view plain copy
我們可以看到,對於擁有重載的f 方法,其native方法名稱後都帶有參數,而沒有重載的g 方法則沒帶有 靜態註冊JNI方法的弊端非常明顯,就是方法名會變得很長,因此下面我們介紹另外一種動態註冊的方法
然後重寫JNI_OnLoad方法(該方法會在Java層通過System.loadLibrary加載完動態鏈接庫後被調用),我們在其中進行動態註冊工作: [cpp] view plain copy
動態註冊的大致步驟如下:
另外,當Java類型為數組時,在標識前會有“[”符號,例如:String[] 類型標識為 [Ljava/lang/String; 下面舉幾個例子: [java] view plain copy
最近了解了關於Java JNI接口的一些關於方法註冊與簽名相關的知識,在此進行一下總結。
使用JNI接口時,我們首先需要把Java方法聲明為native:
[java] view plain copy
- public native void f();
然後編寫對應的C/C++代碼,並編譯成為動態鏈接庫(.dll或.so),在調用Java方法前載入動態鏈接庫即可調用:
[java] view plain copy
- static {
- System.loadLibrary("native-lib");
- }
那麽,Java文件中的native方法是如何與native文件中的方法一一對應的呢?
在此有兩種方法:靜態註冊與動態註冊,下面將一一介紹:
靜態註冊
采用靜態註冊時,Java層的native方法與native層的方法在名稱上具有一一對應的關系,具體要求如下: native層的方法名為:Java_<包名>_<類名>_<方法名>(__<參數>) 其中,包名使用下劃線代替點號進行分割 只有當native方法出現需要重載的時候,native層的方法名後才需要跟上參數(括號裏的內容),參數的編寫形式與JNI簽名相關(後面會介紹) 通常而言,我們可以把native方法集中在一個類中,然後調用: [plain] view plain copy- javah -jni 包名.類名
自動生成對應的c層頭文件 下面是靜態註冊的例子: Java層: [java] view plain copy
- package com.app.superxlcr.jnitest;
- /**
- * Created by superxlcr on 2017/5/25.
- */
- public class NativeTest {
- public native void f();
- public native int f(int a, double b);
- public native void f(Object a, String b);
- public native void g();
- }
native層: [cpp] view plain copy
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class com_app_superxlcr_jnitest_NativeTest */
- #ifndef _Included_com_app_superxlcr_jnitest_NativeTest
- #define _Included_com_app_superxlcr_jnitest_NativeTest
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: com_app_superxlcr_jnitest_NativeTest
- * Method: f
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_com_app_superxlcr_jnitest_NativeTest_f__
- (JNIEnv *, jobject);
- /*
- * Class: com_app_superxlcr_jnitest_NativeTest
- * Method: f
- * Signature: (ID)I
- */
- JNIEXPORT jint JNICALL Java_com_app_superxlcr_jnitest_NativeTest_f__ID
- (JNIEnv *, jobject, jint, jdouble);
- /*
- * Class: com_app_superxlcr_jnitest_NativeTest
- * Method: f
- * Signature: (Ljava/lang/Object;Ljava/lang/String;)V
- */
- JNIEXPORT void JNICALL Java_com_app_superxlcr_jnitest_NativeTest_f__Ljava_lang_Object_2Ljava_lang_String_2
- (JNIEnv *, jobject, jobject, jstring);
- /*
- * Class: com_app_superxlcr_jnitest_NativeTest
- * Method: g
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_com_app_superxlcr_jnitest_NativeTest_g
- (JNIEnv *, jobject);
- #ifdef __cplusplus
- }
- #endif
- #endif
我們可以看到,對於擁有重載的f 方法,其native方法名稱後都帶有參數,而沒有重載的g 方法則沒帶有 靜態註冊JNI方法的弊端非常明顯,就是方法名會變得很長,因此下面我們介紹另外一種動態註冊的方法
動態註冊
使用動態註冊時,我們需要準備好需要自己想要對應的native方法,然後構造JNINativeMethod數組,JNINativeMethod是一種結構體,源碼如下: [cpp] view plain copy- typedef struct {
- // Java層native方法名稱
- const char* name;
- // 方法簽名
- const char* signature;
- // native層方法指針
- void* fnPtr;
- } JNINativeMethod;
然後重寫JNI_OnLoad方法(該方法會在Java層通過System.loadLibrary加載完動態鏈接庫後被調用),我們在其中進行動態註冊工作: [cpp] view plain copy
- JNIEXPORT jint JNICALL
- JNI_OnLoad(JavaVM* vm, void* reserved) {
- JNIEnv *env = NULL;
- jint result = -1;
- // 獲取JNI env變量
- if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
- // 失敗返回-1
- return result;
- }
- // 獲取native方法所在類
- const char* className = "com/app/superxlcr/jnitest/MainActivity";
- jclass clazz = env->FindClass(className);
- if (clazz == NULL) {
- return result;
- }
- // 動態註冊native方法
- if (env->RegisterNatives(clazz, methods, 1) < 0) {
- return result;
- }
- // 返回成功
- result = JNI_VERSION_1_4;
- return result;
- }
動態註冊的大致步驟如下:
- 通過vm(Java虛擬機)參數獲取JNIEnv變量
- 通過FindClass方法找到對應的Java類
- 通過RegisterNatives方法,傳入JNINativeMethod數組,註冊native函數
方法簽名
方法簽名對於區分Java層native重載方法有重大意義 總的來說,方法簽名的組成規則為: [plain] view plain copy- (參數類型標識1參數類型標識2...參數類型標識n)返回值類型標識
類型標識 | Java類型 |
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
L包名/類名; | 各種引用類型 |
V | void |
另外,當Java類型為數組時,在標識前會有“[”符號,例如:String[] 類型標識為 [Ljava/lang/String; 下面舉幾個例子: [java] view plain copy
- // Signature: ()V
- public native void f();
- // Signature: (ID)I
- public native int f(int a, double b);
- // Signature: (Ljava/lang/Object;Ljava/lang/String;)V
- public native void f(Object a, String b);
- // Signature: ()V
- public native void g();
- BufferedReader使用readLine問題
-
有時我們在使用BufferedReader時候會發現使用readLine函數遲遲沒有任何返回,這是因為BufferedReader和BufferedWriter是基於行進行操作的,因此我們使用BufferedWriter的時候使用newLine函數即可,具體代碼如下:
- BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
- writer.write(str);
- writer.newLine();
- writer.flush();
- BufferedReader reader = new BufferedReader(new InputStreamReader(in));
- str = reader.readLine();
JNI 方法註冊與簽名+BufferedReader使用readLine問題