文章目录
之前介绍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");
}
其他的不做介绍了。
参考文章
《》
《》
《》
