Android系統JNI的實現方式,androidjni實現
Android系統JNI的實現方式
All rights reserved
JNI(Java Native Interface)定義了一種Java代碼調用C或者C++代碼等其他代碼的方式。
在Android系統中,JNI通過JNINativeMethod結構體進行描述,該結構體定義於jni.h,如下所示:
typedef struct { const char* name; const char* signature; void* fnPtr;} JNINativeMethod;
第一個參數name:是Java代碼中的函數名。
第二個參數signature:用於描述函數的參數和返回值。
第三個參數fnPtr:C代碼中函數的指標。
其中,第二個參數為一個描述函數參數和返回值的字串,字串的格式如下:
(XX..)X
X的取值和定義如下所示:
|
字元 |
Java類型 |
C類型 |
|
V |
void |
void |
|
Z |
jboolean |
unsigned char |
|
B |
jbyte |
signed char |
|
C |
jchar |
unsigned short |
|
S |
jshort |
short |
|
I |
jint |
int |
|
J |
jlong |
long |
|
F |
jfloat |
float |
|
D |
jdouble |
double |
另外,從jni.h中對於變數類型的定義中也可以看到這些字元的意義,如下所示:
typedef unsigned char jboolean; /* unsigned 8 bits */typedef signed char jbyte; /* signed 8 bits */typedef unsigned short jchar; /* unsigned 16 bits */typedef short jshort; /* signed 16 bits */typedef int jint; /* signed 32 bits */typedef long long jlong; /* signed 64 bits */typedef float jfloat; /* 32-bit IEEE 754 */typedef double jdouble; /* 64-bit IEEE 754 */typedef union jvalue { jboolean z; jbyte b; jchar c; jshort s; jint i; jlong j; jfloat f; jdouble d; jobject l;} jvalue;
例如,為一個驅動添加HAL層,並建立JNI層。僅就JNI層而言,建立frameworks/base/services/jni/com_android_server_DemoService.cpp檔案,該檔案中用於描述JNI介面的代碼如下所示:
static const JNINativeMethod method_table[] = { {"init_native", "()I", (void*)demo_init}, {"setVal_native", "(II)V", (void*)demo_setVal}, {"getVal_native", "(I)I", (void*)demo_getVal}, };
其中“(II)V”,表示函數的有兩個整形參數,返回值為void。
注意:由參數二指定的函數參數和返回值類型一定要和C函數的參數和返回值保持一致,否則雖然編譯能夠通過,但在Android系統載入過程中,會報如下所示的錯誤,導致Android系統無法正常運行。
E/dalvikvm( 1737): ERROR: couldn't find native methodE/dalvikvm( 1737): Requested: Lcom/android/server/DemoService;.init_native:()ZE/dalvikvm( 1737): Candidate: Lcom/android/server/DemoService;.init_native:()IE/JNIHelp ( 1737): RegisterNatives failed for 'com/android/server/DemoService', abortingF/libc ( 1737): Fatal signal 11 (SIGSEGV) at 0xdeadbaad (code=1), thread 1737 (system_server)
以上錯誤通發生在frameworks/base/services/jni/onload.cpp檔案中的JNI_OnLoad()函數中,如下所示:
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved){ JNIEnv* env = NULL; jint result = -1; if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { ALOGE("GetEnv failed!"); return result; } ALOG_ASSERT(env, "Could not retrieve the env!"); …… <strong>register_android_server_DemoService(env);</strong> …… return JNI_VERSION_1_4;}
另外,ProGuard對程式的最佳化也可能導致上述運行錯誤的發生。此時,可以在makefile檔案中添加“LOCAL_PROGUARD_ENABLED:=disabled”宏來關閉ProGuard的最佳化。
關於ProGuard,可以參考其官方網站:http://proguard.sourceforge.net/
ProGuard是一個免費的Java類檔案壓縮器、最佳化器、混淆器和預校正器。它會檢測並刪除沒有用到的類、域、方法以及屬性。它最大限度的最佳化位元組碼並且刪除無用的指令。它用很短的沒有意義的名字對剩餘的類、域和方法進行重新命名。最後,它對處理過的代碼進行預校正。
ProGuard的一些用途如下:
A.為了更小的代碼檔案、更快的網路傳輸、更快的載入速度和更小的記憶體佔用建立更緊湊的代碼;
B.使程式和庫難於進行反向工程;
C.列出無作用程式碼,這樣就能將其刪除;
D.為Java 6或更高的版本對存在的類檔案進行重定位和預校正,以充分利用其快速載入類的效能。