Android media媒体库分析之:MediaProvider
在做Android媒体应用程序时(Audio、Image、Video)需要对Android的媒体提供者(MediaProvider)做详细的分析,下面记录一下我的收获:
一、获取MediaProvider:
该工程在系统源码的packages\providers目录下,提出并导入Eclipse,便于阅读;
图中可见都很多报错的,是滴,因为需要一些系统标准sdk之外的接口,不过不影响我们阅读代码。
二、工程结构及内部关系:
可以从上图看出包含4个文件:
MediaScannerService.Java:媒体服务,配合广播实现媒体扫描类的实例化,数据库的初始化等工作,也向外提供接口;
MediaScannerReceiver.Java:一个广播接收器,用于接受系统发给媒体服务的广播并启动媒体服务;
MediaProvider.Java:媒体数据库的封装类,代码量比较大(四千多行),功能比较复杂,但总的来说就是创建数据库,对外提供URI以实现对数据库的增删改查功能;
MediaThumbRequest.Java:媒体文件缩略图请求类,与MediaProvider配合使用;
上一个关系图更直观一些:
上图不是标准的类图,只是为了梳理逻辑关系画的结构图。
MediaProvider所处的位置及作用见图中红色框中的内容;
上图还包括其他内容:
1、App层:audio、image、video如何与媒体库进行交互;
2、框架层(android.media包下):如何实现媒体的扫描;
3、Native层:如何实现正在的媒体文件解析;
4、资源存储层:sd卡、U盘等介质,DTCM存储缩略图;
三、类详解
1、MediaScannerReceiver:
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Uri uri = intent.getData();
// String externalStoragePath =
// Environment.getExternalStorageDirectory().getPath();
String externalSDStoragePath = Environment
.getExternalSDStorageDirectory().getPath();
String externalUDiskStoragePath = Environment
.getExternalUDiskStorageDirectory().getPath();
String externalExtSDStoragePath = Environment
.getExternalExtSDStorageDirectory().getPath();
if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
// scan internal storage
scan(context, MediaProvider.INTERNAL_VOLUME);
} else {
if (uri.getScheme().equals("file")) {
// handle intents related to external storage
String path = uri.getPath();
if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
if (externalSDStoragePath.equals(path))
scan(context, MediaProvider.EXTERNAL_VOLUME_SD);
else if (externalUDiskStoragePath.equals(path))
scan(context, MediaProvider.EXTERNAL_VOLUME_UDISK);
else if (externalExtSDStoragePath.equals(path))
scan(context, MediaProvider.EXTERNAL_VOLUME_EXTSD);
else
Slog.w(TAG, "unknown volume path " + path);
} else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE)
&& path != null
&& (path.startsWith(externalSDStoragePath + "/")
|| path.startsWith(externalExtSDStoragePath
+ "/") || path
.startsWith(externalUDiskStoragePath + "/"))) {
scanFile(context, path);
}
}
}
}
三类情况需要启动扫描服务:
a、系统启动完成;
b、媒体挂载(EXTERNAL_VOLUME_SD、EXTERNAL_VOLUME_UDISK、EXTERNAL_VOLUME_EXTSD);
c、媒体文件扫描广播(ACTION_MEDIA_SCANNER_SCAN_FILE);
scanFile和scan方法很简单,只是启动媒体服务即可:
private void scan(Context context, String volume) {
Bundle args = new Bundle();
args.putString("volume", volume);
context.startService(new Intent(context, MediaScannerService.class)
.putExtras(args));
}
private void scanFile(Context context, String path) {
Bundle args = new Bundle();
Slog.i(TAG, "Start scanFile.");
args.putString("filepath", path);
context.startService(new Intent(context, MediaScannerService.class)
.putExtras(args));
}
2、MediaScannerService:
第一步:启动一个线程
public void run() {
// reduce priority below other background threads to avoid interfering
// with other services at boot time.
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND
+ Process.THREAD_PRIORITY_LESS_FAVORABLE);
Looper.prepare();
mServiceLooper = Looper.myLooper();
mServiceHandler = new ServiceHandler();
Looper.loop();
}
在线程中拿到当前的消息队列,使用handler处理消息;
第二部:启动ServiceHandler处理消息
ServiceHandler中还是处理两种,一种是扫描,第二种是具体媒体文件的解析;
看一下第二种是如何实现的:
IBinder binder = arguments.getIBinder("listener");
IMediaScannerListener listener = (binder == null ? null
: IMediaScannerListener.Stub.asInterface(binder));
Uri uri = scanFile(filePath,
arguments.getString("mimetype"));
if (listener != null) {
listener.scanCompleted(filePath, uri);
}
代码
private final IMediaScannerService.Stub mBinder = new IMediaScannerService.Stub() {
public void requestScanFile(String path, String mimeType,
IMediaScannerListener listener) {
if (Config.LOGD) {
Log.d(TAG, "IMediaScannerService.scanFile: " + path
+ " mimeType: " + mimeType);
}
Bundle args = new Bundle();
args.putString("filepath", path);
args.putString("mimetype", mimeType);
if (listener != null) {
args.putIBinder("listener", listener.asBinder());
}
startService(new Intent(MediaScannerService.this,
MediaScannerService.class).putExtras(args));
}
public void scanFile(String path, String mimeType) {
requestScanFile(path, mimeType, null);
}
};
那么问题来了:如果我们在App中想让系统媒体库解析具体某一个文件,应该怎么做呢?
从上面代码可以看到,MediaScannerService给我们提供的绑定接口,我们只需要传递filepath和一个IMediaScannerListener listener即可,媒体库在解析完之后会回调scanCompleted方法告诉我们解析结果;
第三步:创建MediaScanner对象,完成扫描和解析;
可见具体扫描、解析工作也不是MediaScannerService做的,MediaScannerService是只在调用sacn、acanfile方法时创建了MediaScanner对象并交给他处理;
MediaScanner在android.media.MediaScanner系统framework里面,这儿就不做讨论了;
MediaScannerService基本就这些内容了;
3、MediaProvider:
MediaProvider就是创建数据库,对外提供URI以实现对数据库的增删改查功能;
4、MediaThumbRequest:
Audio、Image、Video文件都是有缩略图的,缩略图路径存储在DB中,其真实文件存储在sd卡的DICM文件夹下,MediaThumbRequest只是提供给MediaProvider类操作数据库使用。
主要的就两个方法,一个新建缩略图方法:execute,一个更新缩略图方法:updateDatabase
新技能get:应用中获取缩略图,期待下一篇文章;
至此,MediaProvider结构分析清楚了,后续计划补两片文章:
APP中使用系统媒体库;
媒体文件扫描、解析是如何实现的;
见:
更多Android相关信息见Android 专题页面 http://www.linuxidc.com/topicnews.aspx?tid=11
本文永久更新链接地址:http://www.linuxidc.com/Linux/2015-03/114755.htm
Android media媒体库分析之:MediaProvider的更多相关文章
- 如何扫描出Android系统媒体库中视频文件
Android系统启动时会去扫描系统文件,并将系统支持的视频文件(mp4,3gp,wmv)扫描到媒体库(MediaStore)中,下面代码演示如何获得这些文件的信息: publicstatic Lis ...
- Android——媒体库 相关知识总结贴
Android媒体库 http://www.apkbus.com/android-19283-1-1.html Android本地图片选择打开媒体库,选择图片 http://www.apkbus.co ...
- Android加入新的视频格式--媒体库扫描
需求:在mediaprovider数据库中加入.mov后缀格式的视频文件 能够使用工具MediaInfo_GUI_0.7.67_Windows.3243836749.exe 查看mov文件编码格式类型 ...
- Android 插入图片到媒体库
今天介绍一下在Android中怎么插入图片到媒体库,下面看代码: final String titleName = Function.md5(imageUri.toLowerCase()) + &qu ...
- Android 获取视频照片与刷新媒体库
1.获取本地所有视频 public void getLoadMedia() { Cursor cursor = UILApplication.instance.getApplicationContex ...
- Android库分析工具(崩溃反编译)
[时间:2016-07] [状态:Open] [关键词:android, 动态库,静态库, 编译,crash,addr2line] 本文主要整理Android编译系统中可用的库分析工作,可作为后续代码 ...
- MTK Android 源码目录分析
Android 源码目录分析 Android 4.0 |-- abi (application binary interface:应用二进制接口)|-- art (average retrieval ...
- Android 的媒体路由功能应用与框架解析
一.功能描述 Android 的媒体路由API被设计用来允许多种媒体(视频.音乐.图片)在与ANDROID设备连接(无线或有线)的辅助设备(如电视.立体声.家庭戏院系统.音乐播放机)上显示和播放,使用 ...
- Android基础-系统架构分析,环境搭建,下载Android Studio,AndroidDevTools,Git使用教程,Github入门,界面设计介绍
系统架构分析 Android体系结构 安卓结构有四大层,五个部分,Android分四层为: 应用层(Applications),应用框架层(Application Framework),系统运行层(L ...
随机推荐
- CodeFroces--Good Bye 2016-A-New Year and Hurry(水题-模拟)
A. New Year and Hurry time limit per test 1 second memory limit per test 256 megabytes input standar ...
- hdu_1072_Nightmare(BFS)
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1072 题意:给你一个地图,让你在炸弹爆之前找到出口,最初炸弹设定为6,每走一格需要1,中途有地方能让炸 ...
- shell脚本中$
变量名只能包含数字.字母和下划线,因为某些包含其他字符的变量有特殊含义,这样的变量被称为特殊变量. 例如,$ 表示当前Shell进程的ID,即pid ./package/kernel/mac80211 ...
- jsp第1讲(上集)
jsp讲解框架 (一)Java EE核心十三种技术介绍 (二)Java EE程序员修炼成精的法门 (三)jsp版本的用户管理系统演示 (四)jsp概述 (五)jsp的运行原理 (六)jsp版的计算器 ...
- ios做的两个矩形相交叉
#import "ViewController.h" @interface ViewController (){ UIView *_gee; //定义的实例变量 UI ...
- bower 教程
1.运行 - cmd 2.npm install -g bower 3.文件夹右击git bash here
- 杂音 & pop 音的解决方法
杂音 & pop 音的解决方法 1. 喇叭有严重的"吱吱"破音,绝大多数的原因有可能在于V(out)电压不稳定,所以最好测一下无负载时的输出电压.同时也可以测量 VCC – ...
- 对象之function
开头啰嗦几句:程序之中,万事万物皆对象,这就是面向对象编程的核心思想,比如我们的js中的function,其实也是一个对象,是对象就可以有属性,在jq的源码中经常看到类似这样的方法: jQuery.E ...
- Tomcat配置远程调试端口
Tomcat配置远程调试端口 1.Linxu系统: apach/bin/startup.sh开始处中增加如下内容: declare -x CATALINA_OPTS="-server -Xd ...
- NDK编译应用程序
Android源码目录下的build/envsetup.sh文件,描述编译的命令 - m: Makes from the top of the tree. - mm: Build ...