深入理解Android(5)——從MediaScanner分析Android中的JNI,mediascannerjni
前面幾篇介紹了Android中的JNI和基本用法,這一篇我們通過分析Android原始碼中的JNI執行個體,來對JNI部分做一個總結。
一、通向兩個不同世界的橋樑
在前面我們說過,JNI就像一個橋樑,將Java和Native世界緊密的聯絡在了一起,在Android平台上如果沒有Native層的支援我們的系統寸步難行,甚至Java中的虛擬機器也是通過Native實現的。
二、MediaScanner類的簡單介紹
MediaScannerr完成android中的多媒體檔案的掃描工作。例如,mediascanner掃描系統記憶體和SD卡檔案之後,會將掃描的結果載入在資料庫中,在Music這個應用程式中看到的顯示在activity 的list列表上歌曲專輯名,流派,歌曲時間長度等資訊,都是掃描後的結果放在資料庫中,最後讀到的資料庫中的資訊。
MediaScanner這項功能使用到的三種android的基本組件:
1、MediaScannerService(從Service中派生),完成掃描任務,並將掃描結果放入到媒體資料庫中。
2、MediaProvider(ContentProvider派生),針對媒體庫進行相關操作請求,一般情況就是寫,刪,查,更操作。
3、MediaScannerReceiver接收外界的掃描請求。
三、MediaScanner註冊分析
開啟MediaScnner.java可以看到
static { System.loadLibrary("media_jni"); native_init(); }在這裡載入了動態連結程式庫,再調用了native_init()方法
private static native final void native_init();
開啟android_media_MediaScanner.cpp可以看到native_init()的實現
// This function gets a field ID, which in turn causes class initialization.// It is called from a static block in MediaScanner, which won't run until the// first time an instance of this class is used.static voidandroid_media_MediaScanner_native_init(JNIEnv *env){ LOGV("native_init"); jclass clazz = env->FindClass(kClassMediaScanner); if (clazz == NULL) { return; } fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); if (fields.context == NULL) { return; }}上面的這種註冊方式是靜態註冊,其實還有一種動態註冊方式
Java native函數和JNI函數是一一對應的,所以在JNI中,是通過JNINativeMethoid結構來記錄這種關係的。下面就是android_media_MediaScanner.cpp中的動態註冊表。
static JNINativeMethod gMethods[] = { { "processDirectory", "(Ljava/lang/String;Landroid/media/MediaScannerClient;)V", (void *)android_media_MediaScanner_processDirectory }, { "processFile", "(Ljava/lang/String;Ljava/lang/String;Landroid/media/MediaScannerClient;)V", (void *)android_media_MediaScanner_processFile }, { "setLocale", "(Ljava/lang/String;)V", (void *)android_media_MediaScanner_setLocale }, { "extractAlbumArt", "(Ljava/io/FileDescriptor;)[B", (void *)android_media_MediaScanner_extractAlbumArt }, { "native_init", "()V", (void *)android_media_MediaScanner_native_init }, { "native_setup", "()V", (void *)android_media_MediaScanner_native_setup }, { "native_finalize", "()V", (void *)android_media_MediaScanner_native_finalize },};JNINativeMethod結構體如下:
type struct{ const char* name; const char* signature; void* fnPtr;}JNINativeMethod;第一個屬性是Java中native函數的名字
第二個屬性是參數和傳回型別的簽名
第三個屬性是Native對應函數名字
AndroidRunTime類提供了一個registerNativeMethods函數來完成註冊工作,實現如下:
/* * Register native methods using JNI. *//*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods){ return jniRegisterNativeMethods(env, className, gMethods, numMethods);}jniRegisterNativeMethods是Android平台提供的一個協助函數。
在實際的應用中只用兩個函數就可以完成動態註冊工作。
jclass clazz = (*env)->FindClass(env, className);(*env)->RegisterNatives(env, clazz, gMethods, numMethods);
當Java層通過System.loadLibrary加裝玩JNI動態庫後,緊接著會尋找該庫中一個叫JNI_OnLoad的函數。如果有,就調用它,而動態註冊工作就是在這裡完成的。
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("ERROR: GetEnv failed\n"); goto bail; } assert(env != NULL); if (register_android_media_MediaPlayer(env) < 0) { LOGE("ERROR: MediaPlayer native registration failed\n"); goto bail; } if (register_android_media_MediaRecorder(env) < 0) { LOGE("ERROR: MediaRecorder native registration failed\n"); goto bail; } if (register_android_media_MediaScanner(env) < 0) { LOGE("ERROR: MediaScanner native registration failed\n"); goto bail; } if (register_android_media_MediaMetadataRetriever(env) < 0) { LOGE("ERROR: MediaMetadataRetriever native registration failed\n"); goto bail; } if (register_android_media_AmrInputStream(env) < 0) { LOGE("ERROR: AmrInputStream native registration failed\n"); goto bail; } if (register_android_media_ResampleInputStream(env) < 0) { LOGE("ERROR: ResampleInputStream native registration failed\n"); goto bail; } if (register_android_media_MediaProfiles(env) < 0) { LOGE("ERROR: MediaProfiles native registration failed"); goto bail; } if (register_android_mtp_MtpDatabase(env) < 0) { LOGE("ERROR: MtpDatabase native registration failed"); goto bail; } if (register_android_mtp_MtpDevice(env) < 0) { LOGE("ERROR: MtpDevice native registration failed"); goto bail; } if (register_android_mtp_MtpServer(env) < 0) { LOGE("ERROR: MtpServer native registration failed"); goto bail; } /* success -- return valid version number */ result = JNI_VERSION_1_4;bail: return result;}