前面几篇介绍了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;
}

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

  1. 从Android系统出发,分析Android控件构架

    从Android系统出发,分析Android控件构架 Android中所有的控件追溯到根源,就是View 和ViewGroup,相信这个大家都知道,但是大家也许会不太清楚它们之间的具体关系是什么,在A ...

  2. Android Hal层简要分析

    Android Hal层简要分析 Android Hal层(即 Hardware Abstraction Layer)是Google开发的Android系统里上层应用对底层硬件操作屏蔽的一个软件层次, ...

  3. [Android Pro] android root权限破解分析

    许 多机友新购来的Android机器没有破解过Root权限,无法使用一些需要高权限的软件,以及进行一些高权限的操作,其实破解手机Root权限是比较简 单及安全的,破解Root权限的原理就是在手机的/s ...

  4. MTK Android 源码目录分析

    Android 源码目录分析 Android 4.0 |-- abi (application binary interface:应用二进制接口)|-- art (average retrieval ...

  5. Android深入浅出之 AudioTrack分析

    Android深入浅出之Audio 第一部分 AudioTrack分析 一 目的 本文的目的是通过从Audio系统来分析Android的代码,包括Android自定义的那套机制和一些常见类的使用,比如 ...

  6. android MVC && MVP && MVVM分析和对比

    相关:http://www.cnblogs.com/wytiger/p/5305087.html 出处http://blog.csdn.net/self_study,对技术感兴趣的同鞋加群544645 ...

  7. Android Volley源码分析

    今天来顺手分析一下谷歌的volley http通信框架.首先从github上 下载volley的源码, 然后新建你自己的工程以后 选择import module 然后选择volley. 最后还需要更改 ...

  8. Appium Android Bootstrap源码分析之简介

    在上一个系列中我们分析了UiAutomator的核心源码,对UiAutomator是怎么运行的原理有了根本的了解.今天我们会开始另外一个在安卓平台上基于UiAutomator的新起之秀--Appium ...

  9. Android media媒体库分析之:MediaProvider

    在做Android媒体应用程序时(Audio.Image.Video)需要对Android的媒体提供者(MediaProvider)做详细的分析,下面记录一下我的收获: 一.获取MediaProvid ...

随机推荐

  1. Vue组件开发 -- Markdown

    利用marked 和 highlight.js开发markdown组件 实现效果图如下: markdown组件已这种形式<Markdown v-model="markdown" ...

  2. C语言中头文件尖括号和引号的区别

    用include 引用头文件时,双引号和尖括号的区别: 1.双引号:引用非标准库的头文件,编译器首先在程序源文件所在目录查找,如果未找到,则去系统默认目录查找,通常用于引用用户自定义的头文件. 2.尖 ...

  3. Mysql学习总结(28)——MySQL建表规范与常见问题

    一. 表设计 库名.表名.字段名必须使用小写字母,"_"分割. 库名.表名.字段名必须不超过12个字符. 库名.表名.字段名见名知意,建议使用名词而不是动词. 建议使用InnoDB ...

  4. Qt之QSS(动态属性)

    简述 QSS可以定制应用程序的外观,无需关注Qt样式背后的魔力.从非常轻微到极其复杂的调整,样式表都可以做到.对于一个完全定制和独特的用户体验,QtQuick和QGraphicsView是更好的选择. ...

  5. 在使用shape的同一时候,用代码改动shape的颜色属性

    Android里面常常会使用shape来定制一些View的背景 能够改动View的背景颜色.形状等属性 普通情况下.shape都是在xml文件中面写死了.今天遇到一个需求,View的形状是圆角的,可是 ...

  6. jsoncpp-src-0.5.0.tar.gz 源码错误!!!!

    近期在做毕设,使用到了JsonCpp0.5.0版本号的源码! 依照网上的安装配置教程,搭建好环境后就能够使用了! 在这里就不浪费空间去将怎样搭建开发环境了!请大家去google一下就好了! 在解析一个 ...

  7. jsp页面中自定义标签的小演示

    在实习期遇到公司的pg自定义标签了,同事要我自己自学一下 自定义标签是用户定义的JSP语言元素.当JSP页面包含一个自定义标签时将被转化为servlet.JSP标签扩展可以让你创建新的标签并且可以直接 ...

  8. 使用iOS原生sqlite3框架对sqlite数据库进行操作

    摘要: iOS中sqlite3框架可以很好的对sqlite数据库进行支持,通过面向对象的封装,可以更易于开发者使用. 使用iOS原生sqlite3框架对sqlite数据库进行操作 一.引言 sqlit ...

  9. rest_framework_HyperlinkedIdentityField

    #生成链接 HyperlinkedIdentityField class UserInfoSerializer(serializers.ModelSerializer): group = serial ...

  10. 搭建Mysql双机热备 (主从同步)

    准备两台centos7主机:10.0.18.132 master 10.0.18.136  slave 先把selinux关闭,iptables关闭  或者添加端口 132 master安装好Mysq ...