MediaScanner源码JNI介绍

NDK  源码分析  2023年7月6日 am8:09发布1年前 (2023)更新 91es.com站长
67 0 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. 2021: 付志勇:我的秋天没有忧伤(0条评论)
  2. 2021: 史铁生:想念地坛(0条评论)
  3. 2019: 冯友兰:我的读书经验(0条评论)
版权声明 1、 本站名称: 91易搜
2、 本站网址: 91es.com3xcn.com
3、 本站内容: 部分来源于网络,仅供学习和参考,若侵权请留言
3、 本站申明: 个人流水账日记,内容并不保证有效

暂无评论

暂无评论...

随机推荐

Android分析Monkey日志

推荐使用我的个人导航大全:编程导航 、IT导航、极客导航、读书导航Log 在android中的地位非常重要,要是作为一个android程序员不能过分析log这关,算是android没有入门吧 。 下面我们就来说说如何处理log文件 。什么时候会有Log文件的产生 ?Log的产生大家都...

陆文夫:脚步声

照理不应该被自己的脚步声吓住,因为在少年时我就在黑暗无人的旷野间听到过此种脚步。那时我住在江边的一个水陆码头上,那里没有学校,只有二里路外的村庄上有一位塾师在那里授馆,我只能去那里读书。那位塾师要求学生们苦读,即使不头悬梁、锥刺股,却也要“闻鸡起舞”,所谓闻鸡起舞就是在鸡鸣时分赶到学塾里去读早书。农...

系统应用MediaButton的使用方式

前言这个是Android高版本注册监听MediaButton的使用,没有测试,至于测试普通应用,会重新整理一篇文章。不想误人子弟,文章大部分设置为私人查阅,抱歉正文隐藏内容!付费阅读后才能查看!¥2 ¥3多个隐藏块只需支付一次付费阅读参考文章这个是从同事那拷贝的,没有测试。

java.lang.ClassNotFoundException

在工作中遇到Caused by: java.lang.ClassNotFoundException: Didn’t find class “XXX” on path: DexPathList[[zip file“/data/app/net.sourceforge.simcpux-2.apk”],n...

adb shell 发送广播带包名

前言之前我们知道adb发送广播带参数,但是有于Android的权限慢慢收紧,如果不加上接收应用的包名,无法收到发送的静态广播。Android 8.0Beginning with Android 8.0 (API level 26), the system imposes additiona...

隐藏Settings中的Preference笔记

在Android项目开发中,客户要求精简Settings,也就是删除部分不需要或者无用的功能,因此需要影藏Preference等,下面是摘抄网友并自己加工整理的。隐藏 Preference 有两种方法:一、先在xml布局里面删,然后在java里面删掉调用的相关部分,但如果很多地方都有调...