android 動態注冊JNI函數過程源碼分析


以MediaRecorder為例介紹android中,java層的native函數是如何能找到對應的jni層的函數的。
 
MediaRecorder.java中,生命了這樣的一個方法
private static native final void native_init();

 

那是怎么知道是這個方法的呢?
 
MediaRecorder.java位於android.media包中,所以native_init的全路徑是android.media.MediaRecorder.native_init。顯而易見,將“.”替換成“_”就是jni函數的名字了。這里說的就是JNI函數的注冊問題,就是將java層的native函數和JNI層函數的具體實現關聯起來。
 
JNI函數的注冊有兩種方法,一種是靜態方法,需要用javah為每個聲明了native函數的java類編譯出的class文件生成一個頭文件,另一種是動態注冊,通過數據結構保存關聯關系實現注冊,這里主要介紹動態注冊。
 
動態注冊
動態注冊需要一個數據結構去保存相關的關聯關系,這個結構(在jni.h中聲明)是:
typedef struct {
 
 
const char* name; //java中native函數的名字
 
 
const char* signature; //Java函數的簽名信息,用字符串表示,是參數類型和返回值類型的組合
 
 
void* fnPtr; } JNINativeMethod; //JNI層函數的函數指針,void*類型

 

 
 
在JNI層中(android_media_MediaRecorder.cpp)是如何使用這個結構體的:
 
static JNINativeMethod gMethods[] = {
 
{"native_init","()V",(void *)android_media_MediaRecorder_native_init},
 
};
 
// This function only registers the native methods, and is called from
// JNI_OnLoad in android_media_MediaPlayer.cpp
int register_android_media_MediaRecorder(JNIEnv *env) //什么時候會調用呢?后面介紹
{
return AndroidRuntime::registerNativeMethods(env,
"android/media/MediaRecorder", gMethods, NELEM(gMethods));
}

 

利用AndroidRuntime類提供的registerNativeMethods函數完成注冊工作
 
/*
* Register native methods using JNI.
*/
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
const char* className, const JNINativeMethod* gMethods, int numMethods) //位於AndroidRuntime.cpp
{
return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
 
/*
* Register native JNI-callable methods.
*
* "className" looks like "java/lang/String".
*/
int jniRegisterNativeMethods(JNIEnv* env, const char* className,const JNINativeMethod* gMethods, int numMethods) //位於JNIHelp.c
{ jclass clazz;
LOGV("Registering %s natives\n", className);
clazz = (*env)->FindClass(env, className);
if (clazz == NULL)
{
LOGE("Native registration unable to find class '%s'\n", className); return -1;
}
if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0)
{
LOGE("RegisterNatives failed for '%s'\n", className); return -1;
} return 0;}

 

 
 
實際上所有的注冊工作最后就是調用:
(*env)->RegisterNatives(env,clazz,gMethods,numMethods)完成。那么這些動態注冊的函數是什么時候和什么地方調用的呢?其實,當Java層通過System.loadLibrary加載完JNI動態庫之后,就會查找庫中叫JNI_OnLoad函數,通過調用它完成動態注冊的工作。
externint register_android_media_MediaRecorder(JNIEnv *env);
jint JNI_OnLoad(JavaVM* vm, void* reserved) //JavaVM* vm是Java虛擬機在JNI層的對象,每一個java進程只有一個JVM
{
JNIEnv* env = NULL; //每一個線程有一個JNIEnv對象
jint result = -1;
 
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed\n");
goto bail;
}
 
 
if (register_android_media_MediaRecorder(env) < 0) { //動態注冊MediaRecorder的JNI函數
LOGE("ERROR: MediaRecorder native registration failed\n");
goto bail;
} /* success -- return valid version number */
result = JNI_VERSION_1_4;
 
bail:
return result;
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM