深入瞭解android平臺的jni---註冊native函數

註冊native函數有兩種方法:靜態註冊和動態註冊。
1、靜態註冊方法
根據函數名找到對應的JNI函數:Java層調用函數時,會從對應的JNI中尋找該函數,如果沒有就會報錯,如果存在則會建立一個關聯聯繫,以後在調用時會直接使用這個函數,這部分的操作由虛擬機完成。
靜態方法就是根據函數名來遍歷java和jni函數之間的關聯,而且要求jni層函數的名字必須遵循
特定的格式,其缺點在於:
1)javah生成的jni層函數特別長;
2)初次調用native函數時要根據名字搜索對應的jni層函數來建立關聯聯繫,這樣影響效率。
2、動態註冊方法
JNI 允許你提供一個函數映射表,註冊給Jave虛擬機,這樣Jvm就可以用函數映射表來調用相應的函數,
就可以不必通過函數名來查找需要調用的函數了。
Java與JNI通過JNINativeMethod的結構來建立聯繫,它在jni.h中被定義,其結構內容如下:
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
第一個變量name是Java中函數的名字。
第二個變量signature,用字符串是描述了函數的參數和返回值
第三個變量fnPtr是函數指針,指向C函數。
當java通過System.loadLibrary加載完JNI動態庫後,緊接着會查找一個JNI_OnLoad的函數,如果有,就調用它,
而動態註冊的工作就是在這裏完成的。
1)JNI_OnLoad()函數
JNI_OnLoad()函數在VM執行System.loadLibrary(xxx)函數時被調用,它有兩個重要的作用:
指定JNI版本:告訴VM該組件使用那一個JNI版本(若未提供JNI_OnLoad()函數,VM會默認該使用最老的JNI 1.1版),如果要使用新版本的JNI,
例如JNI 1.4版,則必須由JNI_OnLoad()函數返回常量JNI_VERSION_1_4(該常量定義在jni.h中) 來告知VM。
初始化設定,當VM執行到System.loadLibrary()函數時,會立即先呼叫JNI_OnLoad()方法,因此在該方法中進行各種資源的初始化操作最爲恰當,
2)RegisterNatives
RegisterNatives在AndroidRunTime裏定義
syntax:
jint RegisterNatives(jclass clazz, const JNINativeMethod* methods,jint nMethods)
3、在android中加入自定義的native函數
JNI在Android層次結構中的作用如下圖所示:
深入瞭解android平臺的jni---註冊native函數
在Android中,主要的JNI代碼在以下的路徑中:
Android源碼根目錄/frameworks/base/core/jni/
這個路徑中的內容將被編譯成庫libandroid_runtime.so,這就是一個普通的動態庫,被放置在目標系統的/system/lib目錄中.除此之外,Android還包含其他的JNI庫,例如,媒體部分的JNI目錄frameworks/base/media/jni/中,被編譯成庫libmedia_jni.so.
JNI中的各個文件實際上就是C++的普通文件,其命名一般和支持的Java類有對應關係。
這種關係是習慣上的寫法,而不是強制的。
1)註冊JNI方法
在Android源碼根目錄/frameworks/base/services/jni/目錄下有一個onload.cpp文件,加入 jni函數申明和jni函數註冊方法
#include "JNIHelp.h"
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"
namespace android {
int register_android_server_AlarmManagerService(JNIEnv* env);
int register_android_server_BatteryService(JNIEnv* env);
int register_android_server_InputApplicationHandle(JNIEnv* env);
int register_android_server_InputWindowHandle(JNIEnv* env);
int register_android_server_InputManager(JNIEnv* env);
int register_android_server_LightsService(JNIEnv* env);
int register_android_server_PowerManagerService(JNIEnv* env);
int register_android_server_UsbDeviceManager(JNIEnv* env);
int register_android_server_UsbHostManager(JNIEnv* env);
int register_android_server_VibratorService(JNIEnv* env);
int register_android_server_SystemServer(JNIEnv* env);
int register_android_server_location_GpsLocationProvider(JNIEnv* env);
int register_android_server_connectivity_***(JNIEnv* env);
int register_android_server_HelloService(JNIEnv *env); //此處加入自定義jni函數申明
};
using namespace android;
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) {
LOGE("GetEnv failed!");
return result;
}
LOG_ASSERT(env, "Could not retrieve the env!");
register_android_server_PowerManagerService(env);
register_android_server_InputApplicationHandle(env);
register_android_server_InputWindowHandle(env);
register_android_server_InputManager(env);
register_android_server_LightsService(env);
register_android_server_AlarmManagerService(env);
register_android_server_BatteryService(env);
register_android_server_UsbDeviceManager(env);
register_android_server_UsbHostManager(env);
register_android_server_VibratorService(env);
register_android_server_SystemServer(env);
register_android_server_location_GpsLocationProvider(env);
register_android_server_connectivity_***(env);
register_android_server_HelloService(env); //jni方法註冊
return JNI_VERSION_1_4;
}
onload.cpp文件上部分爲註冊函數的聲明,下部分爲調用各種註冊函數,而這些註冊函數就是JNI方法的註冊函數! 正是通過這些註冊函數,上層才能調用註冊的JNI方法.
以register_android_server_HelloService爲例,來看一個註冊函數的具體實現過程是如何的。
打開com_android_service_HelloService.cpp文件
2)加入註冊函數的實現代碼,如下:
int register_android_server_HelloService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));
}
#其中jniRegisterNativeMethods爲註冊JNI方法函數,
#此函數的第二個參數爲對應着java類即HelloService.java的文件名,第三個參數爲註冊的方法表
3)加入jni方法表
static const JNINativeMethod method_table[] = {
{"init_native", "()Z", (void*)hello_init},
{"setVal_native", "(I)V", (void*)hello_setVal},
{"getVal_native", "()I", (void*)hello_getVal},
};
4)方法表內各個接口的實現代碼
static void hello_setVal(JNIEnv* env, jobject clazz, jint value) {
val = value;
LOGI("Hello JNI: set value %d to device.", val);
}
static jint hello_getVal(JNIEnv* env, jobject clazz) {
LOGI("Hello JNI: get value %d from device.", val);
return val;
}
static jboolean hello_init(JNIEnv* env, jclass clazz) {
LOGI("Hello JNI: initializing......");
return -1;
}
完整代碼如下:
namespace android
{
int val;
static void hello_setVal(JNIEnv* env, jobject clazz, jint value) {
val = value;
LOGI("Hello JNI: set value %d to device.", val);
}
static jint hello_getVal(JNIEnv* env, jobject clazz) {
LOGI("Hello JNI: get value %d from device.", val);
return val;
}
static jboolean hello_init(JNIEnv* env, jclass clazz) {
LOGI("Hello JNI: initializing......");
return -1;
}
static const JNINativeMethod method_table[] = {
{"init_native", "()Z", (void*)hello_init},
{"setVal_native", "(I)V", (void*)hello_setVal},
{"getVal_native", "()I", (void*)hello_getVal},
};
int register_android_server_HelloService(JNIEnv *env) {
return jniRegisterNativeMethods(env, "com/android/server/HelloService", method_table, NELEM(method_table));
}
}
本文歡迎轉載,轉載請註明出處與作者
出處:http://blog.sina.com.cn/staratsky
作者:流星


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章