MediaScanner源码JNI介绍

NDK  源码分析  2023年7月6日 am8:09发布1年前 (2023)更新 城堡大人
92 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、 本站名称: 笔友城堡
2、 本站网址: https://www.biumall.com/
3、 本站部分文章来源于网络,仅供学习与参考,如有侵权,请留言

暂无评论

暂无评论...

随机推荐

Android动画之RotateAnimation

Android动画View Animation 视图动画(Tween Animation 补间动画),只能用来设置View的动画Drawable Animation 帧动画(Frame动画),一帧帧地显示资源文件中的DrawableProperty Animation 属性动画,在andr...

关闭Google chrome 更新提示

前言Win7电脑每次打卡Google Chrome,就会提示若要接收后续 Google Chrome 更新,您需使用 Windows 10 或更高版本。该计算机目前使用的是 Windows 7不会自动关闭,都需要手动点一下,很烦。因此在网上如何关闭这提示的方法,记录一下。正文下面是亲测...

bootchart 的配置和使用

前言这里记录一下bootchart 的配置和使用,方便自己查阅。正文安装必要工具在设备端使能bootchart,抓取数据在服务器上解析bootchart数据,绘制成图分析bootchart.png安装必要工具略,由于公司编译代码的服务器是可以使用bootchart 命令,这里就...

何其芳:脚步

你的脚步常低响在我的记忆中,在我深思的心上踏起甜蜜的凄动。有如虚阁悬琴,久失去了亲切的手指,黄昏风过,弦弦犹颤着昔日的声息;又如白杨的落叶飘在无言的荒郊,片片互递的叹息犹似树上的萧萧。呵,那是江南的秋夜!深秋正梦得酣熟,而又清彻,脆薄,如不胜你低抑之脚步!你是怎样悄悄地扶上曲...

Vim命令简单介绍

一:复制命令y: 复制在可视模式下选中的文本yy or Y: 复制整行文本y[n]w: 复制一(n)个词y[n]l: 复制光标右边1(n)个字符y[n]h: 复制光标左边1(n)个字符y$: 从光标当前位置复制到行尾y0: 从光标当前位置复制到行首y1G或ygg: 复制光标以上的所有...

[摘]Android如何设置TextView的行间距、行高。

在Android系统中TextView默认行间距比较窄,不美观。我们可以设置每行的行间距,可以通过属性android:lineSpacingExtra或android:lineSpacingMultiplier来做。修改行间距、行高你可以使用如下TextView属性1、android:lin...