MediaScanner源码JNI介绍

NDK开发 3XCN.com站长2023年7月6日 am8:09发布1年前 (2023)更新
0
导航号,我的单页导航
目录

前言

之前介绍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中用的是动态注册,动态注册只需要几个步骤[大致吧]

  1. 获取Java类路径

  2. 在JNI创建对应native方法

  3. 定义Java函数和JNI方法的绑定表

  4. 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的绑定表

{(方法名,方法签名,函数指针},至于不懂的,可以看看《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中对应的方法。

具体可以看《JNI调用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");
}

其他的不做介绍了。

参考文章

  1. MediaScanner源码分析

  2. JNI动态注册

  3. JNI调用Java方法

版权声明 1、 本站名称: 91易搜
2、 本站网址: https://www.91es.com/
3、 本站文章: 部分来源于网络,仅供站长学习和参考,若侵权请留言
导航号,我的单页导航

暂无评论

评论审核已启用。您的评论可能需要一段时间后才能被显示。

暂无评论...