深入理解Android(5)——从MediaScanner分析Android中的JNI

前面几篇介绍了Android中的JNI和基本用法,这一篇我们通过分析Android源代码中的JNI实例,来对JNI部分做一个总结。

一、通向两个不同世界的桥梁

在前面我们说过,JNI就像一个桥梁,将Java和Native世界紧密的联系在了一起,在Android平台上如果没有Native层的支持我们的系统寸步难行,甚至Java中的虚拟机也是通过Native实现的。

二、MediaScanner类的简单介绍

MediaScannerr完成android中的多媒体文件的扫描工作。例如,mediascanner扫描系统内存和SD卡文件之后,会将扫描的结果加载在数据库中,在Music这个应用程序中看到的显示在activity 的list列表上歌曲专辑名,流派,歌曲时长等信息,都是扫描后的结果放在数据库中,最后读到的数据库中的信息。

MediaScanner这项功能使用到的三种android的基本组件:

1、MediaScannerService(从Service中派生),完成扫描任务,并将扫描结果放入到媒体数据库中。

2、MediaProvider(ContentProvider派生),针对媒体库进行相关操作请求,一般情况就是写,删,查,更操作。

3、MediaScannerReceiver接收外界的扫描请求。

三、MediaScanner注册分析

打开MediaScnner.java可以看到

    static {
        System.loadLibrary("media_jni");
        native_init();
    }

在这里加载了动态链接库,再调用了native_init()方法

private static native final void native_init();

打开android_media_MediaScanner.cpp可以看到native_init()的实现

// This function gets a field ID, which in turn causes class initialization.
// It is called from a static block in MediaScanner, which won‘t run until the
// first time an instance of this class is used.
static void
android_media_MediaScanner_native_init(JNIEnv *env)
{
    LOGV("native_init");
    jclass clazz = env->FindClass(kClassMediaScanner);
    if (clazz == NULL) {
        return;
    }

    fields.context = env->GetFieldID(clazz, "mNativeContext", "I");
    if (fields.context == NULL) {
        return;
    }
}

上面的这种注册方式是静态注册,其实还有一种动态的注册方式

Java native函数和JNI函数是一一对应的,所以在JNI中,是通过JNINativeMethoid结构来记录这种关系的。下面就是android_media_MediaScanner.cpp中的动态注册表。

static 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;)V",
        (void *)android_media_MediaScanner_processFile
    },

    {
        "setLocale",
        "(Ljava/lang/String;)V",
        (void *)android_media_MediaScanner_setLocale
    },

    {
        "extractAlbumArt",
        "(Ljava/io/FileDescriptor;)[B",
        (void *)android_media_MediaScanner_extractAlbumArt
    },

    {
        "native_init",
        "()V",
        (void *)android_media_MediaScanner_native_init
    },

    {
        "native_setup",
        "()V",
        (void *)android_media_MediaScanner_native_setup
    },

    {
        "native_finalize",
        "()V",
        (void *)android_media_MediaScanner_native_finalize
    },
};

JNINativeMethod结构体如下:

type struct{
    const char* name;
    const char* signature;
    void* fnPtr;
}JNINativeMethod;

第一个属性是Java中native函数的名字

第二个属性是参数和返回类型的签名

第三个属性是Native对应函数名字

AndroidRunTime类提供了一个registerNativeMethods函数来完成注册工作,实现如下:

/*
 * Register native methods using JNI.
 */
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
    const char* className, const JNINativeMethod* gMethods, int numMethods)
{
    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}

jniRegisterNativeMethods是Android平台提供的一个帮助函数。

在实际的应用中只用两个函数就可以完成动态注册工作。

jclass clazz = (*env)->FindClass(env, className);
(*env)->RegisterNatives(env, clazz, gMethods, numMethods);

当Java层通过System.loadLibrary加装玩JNI动态库后,紧接着会查找该库中一个叫JNI_OnLoad的函数。如果有,就调用它,而动态注册工作就是在这里完成的。

jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("ERROR: GetEnv failed\n");
        goto bail;
    }
    assert(env != NULL);

    if (register_android_media_MediaPlayer(env) < 0) {
        LOGE("ERROR: MediaPlayer native registration failed\n");
        goto bail;
    }

    if (register_android_media_MediaRecorder(env) < 0) {
        LOGE("ERROR: MediaRecorder native registration failed\n");
        goto bail;
    }

    if (register_android_media_MediaScanner(env) < 0) {
        LOGE("ERROR: MediaScanner native registration failed\n");
        goto bail;
    }

    if (register_android_media_MediaMetadataRetriever(env) < 0) {
        LOGE("ERROR: MediaMetadataRetriever native registration failed\n");
        goto bail;
    }

    if (register_android_media_AmrInputStream(env) < 0) {
        LOGE("ERROR: AmrInputStream native registration failed\n");
        goto bail;
    }

    if (register_android_media_ResampleInputStream(env) < 0) {
        LOGE("ERROR: ResampleInputStream native registration failed\n");
        goto bail;
    }

    if (register_android_media_MediaProfiles(env) < 0) {
        LOGE("ERROR: MediaProfiles native registration failed");
        goto bail;
    }

    if (register_android_mtp_MtpDatabase(env) < 0) {
        LOGE("ERROR: MtpDatabase native registration failed");
        goto bail;
    }

    if (register_android_mtp_MtpDevice(env) < 0) {
        LOGE("ERROR: MtpDevice native registration failed");
        goto bail;
    }

    if (register_android_mtp_MtpServer(env) < 0) {
        LOGE("ERROR: MtpServer native registration failed");
        goto bail;
    }

    /* success -- return valid version number */
    result = JNI_VERSION_1_4;

bail:
    return result;
}
时间: 2024-08-02 15:49:53

深入理解Android(5)——从MediaScanner分析Android中的JNI的相关文章

理解boot.img与逆向分析Android/linux内核

一些尝试和理解. 1>提取boot.img: 其中,msm代表是高通的芯片,msm_sdcc.1是外接的SD卡挂载的目录,by-name指的是这个sd卡分区的名称.下面几行代表每个分区存储的东西. 记得提前su,dd if=/dev/block/mmcblk0p8 of=/data/local/tmp/boot.img.将boot.img dump出来 adb root获得root权限,将boot.img 移到pc上. 2>boot.img格式分析 如system/core/mkbootimg

Android Launcher2源码分析

Android   Launcher2源码分析 Android源码程序程序中有一个应用程序入口,官方给出的中文翻译为"启动器".我们一下统称Launcher. Launcher源码分析,我们还是从AndroidManifest.xml开始: <application android:name="com.android.launcher2.LauncherApplication" android:label="@string/application_n

Android深入源码分析理解Aidl整体调用流程(雷惊风)

2017年开始上班的第一天,老不想工作了,假期感觉还没开始就已经结束了,唉,时间就是这样,新的一年开始了,虽然很不想干正事,没办法,必须干起来,因为后边的路还很长,距离六十岁还很远.刚上班也没什么事,复习一下之前的东西,看了一下Aidl相关的知识,仔细瞅了瞅Aidl的调用流程,这里写篇文章整理一下,帮助大家的同时,自己也加深一下印象.对Aidl不太了解的童鞋可以先看一下我之前的一篇文章, 链接如下:http://blog.csdn.net/liuyonglei1314/article/detai

Android深入浅出之 AudioTrack分析

Android深入浅出之Audio 第一部分 AudioTrack分析 一 目的 本文的目的是通过从Audio系统来分析Android的代码,包括Android自定义的那套机制和一些常见类的使用,比如Thread,MemoryBase等. 分析的流程是: l         先从API层对应的某个类开始,用户层先要有一个简单的使用流程. l         根据这个流程,一步步进入到JNI,服务层.在此过程中,碰到不熟悉或者第一次见到的类或者方法,都会解释.也就是深度优先的方法. 1.1 分析工

Android加壳原理分析

0x00 阅读本文前,建议读者首先阅读Android加壳原理,参考文章Android中的Apk的加固(加壳)原理解析和实现.如果没有看过这篇文章,本文理解起来比较困难. 0x01 下面我们来分析脱壳代码为什么要这样写,核心脱壳代码在ProxyApplication类里面,首先执行成员方法attachBaseContext,然后执行成员方法onCreate. 那么attachBaseContext是什么时候被执行的呢,为什么先于onCreate执行呢?那就需要看Android的源码了,我们选用的是

Android内存泄露案例分析

一款优秀的Android应用,不仅要有完善的功能,也要有良好的体验,而性能是影响体验的一个重要因素.内存泄露是Android开发中常见的性能问题.这篇文章,通过我们曾经遇到的一个真实的案例,来讲述一个内存泄露问题,从发现到分析定位,再到最终解决的全过程. 这里把整个过程分为四个阶段: 第一阶段,现场勘查,分析Bug现象,找出有用线索: 第二阶段,初步推断,根据之前的线索,推断可能导致Bug的原因,并且进一步验证推断是否正确: 第三阶段,探究根源,找出导致Bug的真正原因: 第四阶段,解决方案,研

[Android]Fragment源码分析(一) 构造

Fragment是Android3.0之后提供的api,被大家广泛所熟知的主要原因还是因为随即附带的ViewPager控件.虽然我并不喜欢用它,但是它确实是一个相对不错的控件.还是我的一贯作风,我将从源码上向大家展示什么是Fragment.我们先写一个简单的代码对Fragment有个直观的认识:(为了保证我们方便调试,我们可以直接使用V4提供的源码包) FragmentTransaction t = getSupportFragmentManager().beginTransaction();

android:clipChildren属性的分析——是否剪裁子View

MainActivity如下: package cc.testclipchildren; import android.os.Bundle; import android.app.Activity; /** * android:clipChildren属性的分析 * 该属性默认值为android:clipChildren="true" * 单从字面意思理解clipChildren的意思是:裁剪(缩短)孩子 * 我们将其值设置为false后那么当子控件的高度高于父控件时 * 也会完全显示

Android media媒体库分析之:MediaProvider

在做Android媒体应用程序时(Audio.Image.Video)需要对Android的媒体提供者(MediaProvider)做详细的分析,下面记录一下我的收获: 一.获取MediaProvider: 该工程在系统源码的packages\providers目录下,提出并导入Eclipse,便于阅读: 图中可见都很多报错的,是滴,因为需要一些系统标准sdk之外的接口,不过不影响我们阅读代码. 二.工程结构及内部关系: 可以从上图看出包含4个文件: MediaScannerService.Ja