目录
前言
之前介绍MediaScanner源码时就知道这里用了JNI,学习完JNI后重跟一下MediaScanner的代码。记录一下本地方法的使用和调用Java方法,记录于此,方便自己查阅和复现。
正文
涉及文件或目录
frameworks\base\media\java\android\media\MediaScanner.java frameworks\base\media\jni\android_media_MediaScanner.cpp
MediaScanner.java
在MediaScanner.java中,加载了libmedia_jni.so,并调用native_init()和native_setup()和setLocale()等方法进行配置和初始化。
static { System.loadLibrary("media_jni"); native_init(); }
public MediaScanner(Context c, String volumeName) { native_setup(); //略[] setLocale(language); }
处理上面三个的native 方法,还有
//遍历目录 private native void processDirectory(String path, MediaScannerClient client); //解析音视频文件 private native boolean processFile(String path, String mimeType, MediaScannerClient client); //解析专辑图 public native byte[] extractAlbumArt(FileDescriptor fd); //结束时通知释放 private native final void native_finalize();
android_media_MediaScanner.cpp
frameworks\base\media\目录下有专门放JNI的目录,可以根据包名进行查找对应的cpp文件
MediaScanner.java所在的包名
package android.media;
所以可以推断出需要看的文件。
android_media_MediaScanner.cpp中用的是动态注册,动态注册只需要几个步骤[大致吧]
-
获取Java类路径
-
在JNI创建对应native方法
-
定义Java函数和JNI方法的绑定表
-
JNI_OnLoad()注册本地方法
MediaScanner类路径
包名+类名
static const char* const kClassMediaScanner = "android/media/MediaScanner";
JNI创建native方法
因为好几个,这里以processDirectory()为例。
static void android_media_MediaScanner_processDirectory( JNIEnv *env, jobject thiz, jstring path, jobject client){ MediaScanner *mp = getNativeScanner_l(env, thiz); if (path == NULL) { jniThrowException(env, kIllegalArgumentException, NULL); return; } const char *pathStr = env->GetStringUTFChars(path, NULL); if (pathStr == NULL) { // Out of memory return; } MyMediaScannerClient myClient(env, client); MediaScanResult result = mp->processDirectory(pathStr, myClient); env->ReleaseStringUTFChars(path, pathStr); }
Java和JNI的绑定表
{(方法名,方法签名,函数指针},至于不懂的,可以看看《》
static const 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;)Z", (void *)android_media_MediaScanner_processFile }, { "extractAlbumArt", "(Ljava/io/FileDescriptor;)[B", (void *)android_media_MediaScanner_extractAlbumArt }, //略 };
JNI_OnLoad()注册
// This function only registers the native methods, and is called from // JNI_OnLoad in android_media_MediaPlayer.cpp int register_android_media_MediaScanner(JNIEnv *env) { return AndroidRuntime::registerNativeMethods(env, kClassMediaScanner, gMethods, NELEM(gMethods)); }
上面就是注册,为啥不是JNI_OnLoad(),因为这个方法在其他地方调用了。上面也有备注,看android_media_MediaPlayer.cpp,如下,确实有JNI_OnLoad()
jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) { JNIEnv* env = NULL; jint result = -1; //略 if (register_android_media_MediaScanner(env) < 0) { ALOGE("ERROR: MediaScanner native registration failed\n"); goto bail; } //略 /* success -- return valid version number */ result = JNI_VERSION_1_4; bail: return result; }
JNI调用Java方法
这个主要涉及MyMediaScannerClient(父类MediaScannerClient)。
# java中的 public interface MediaScannerClient { public void scanFile(String path, long lastModified, long fileSize, boolean isDirectory, boolean noMedia); public void handleStringTag(String name, String value); public void setMimeType(String mimeType); }
JNI层也有一样的,然后在android_media_MediaScanner.cpp中一样实现了MyMediaScannerClient
#android_media_MediaScanner.cpp中 MyMediaScannerClient(JNIEnv *env, jobject client) : mEnv(env), mClient(env->NewGlobalRef(client)), mScanFileMethodID(0), mHandleStringTagMethodID(0), mSetMimeTypeMethodID(0) { ALOGW("MyMediaScannerClient constructor"); jclass mediaScannerClientInterface = env->FindClass(kClassMediaScannerClient); if (mediaScannerClientInterface == NULL) { ALOGE("Class %s not found", kClassMediaScannerClient); } else { mScanFileMethodID = env->GetMethodID( mediaScannerClientInterface, "scanFile", "(Ljava/lang/String;JJZZ)V"); mHandleStringTagMethodID = env->GetMethodID( mediaScannerClientInterface, "handleStringTag", "(Ljava/lang/String;Ljava/lang/String;)V"); mSetMimeTypeMethodID = env->GetMethodID( mediaScannerClientInterface, "setMimeType", "(Ljava/lang/String;)V"); } }
这里获取几个Java方法的MethodID,然后可以通过MethodID进行调用Java中对应的方法。
具体可以看《》,下面的scanFile()就调用了Java层的scanFile。
virtual status_t scanFile(const char* path, long long lastModified, long long fileSize, bool isDirectory, bool noMedia){ //略 //调用的也是java层的scanFile方法 mEnv->CallVoidMethod(mClient, mScanFileMethodID, pathStr, lastModified, fileSize, isDirectory, noMedia); return checkAndClearExceptionFromCallback(mEnv, "scanFile"); }
其他的不做介绍了。
参考文章
-
《》
-
《》
-
《》