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中使用系统媒体库; 
媒体文件扫描、解析是如何实现的;

见:

  1. http://www.linuxidc.com/Linux/2015-03/114754.htm
  2. http://www.linuxidc.com/Linux/2015-03/114756.htm

更多Android相关信息见Android 专题页面 http://www.linuxidc.com/topicnews.aspx?tid=11

本文永久更新链接地址http://www.linuxidc.com/Linux/2015-03/114755.htm

时间: 2024-08-05 23:30:34

Android media媒体库分析之:MediaProvider的相关文章

如何扫描出Android系统媒体库中视频文件

Android系统启动时会去扫描系统文件,并将系统支持的视频文件(mp4,3gp,wmv)扫描到媒体库(MediaStore)中,下面代码演示如何获得这些文件的信息: publicstatic List<VideoInfo> sysVideoList = null;// 视频信息集合 sysVideoList = new ArrayList<VideoInfo>();   setVideoList(); privatevoid setVideoList() {   // Media

Android加入新的视频格式--媒体库扫描

需求:在mediaprovider数据库中加入.mov后缀格式的视频文件 能够使用工具MediaInfo_GUI_0.7.67_Windows.3243836749.exe 查看mov文件编码格式类型,能够到http://download.csdn.net/detail/deng0zhaotai/8767089下载. 概要 完整名称 : D:\好歌曲学员合.mov 文件格式 : MPEG-4 格式简单介绍 : QuickTime 编码设置ID : qt 文件大小 : 83.8 MiB 长度 :

Android添加新的视频格式--媒体库扫描

需求:在mediaprovider数据库中添加.mov后缀格式的视频文件 可以使用工具MediaInfo_GUI_0.7.67_Windows.3243836749.exe 查看mov文件编码格式类型,可以到http://download.csdn.net/detail/deng0zhaotai/8767089下载. 概要 完整名称 : D:\好歌曲学员合.mov 文件格式 : MPEG-4 格式简介 : QuickTime 编码设置ID : qt 文件大小 : 83.8 MiB 长度 : 1分

Android 之 向媒体库添加新媒体

在默认的情况下.由自己的程序创建,并存储在应用程序私有文件夹中的媒体文件,对其他应用程序来说是不可用的.要想这些媒体文件可以被其他应用程序使用.需要把他们添加到媒体库中,目前有两种方法可以实现. 第一种:使用媒体扫描仪添加到媒体库. /** * 扫描指定目录了的文件,添加到媒体库 * * @param filePath * 路径 */ private void scanMedia(final String filePath) { MediaScannerConnectionClient medi

【Android开发经验】如何获取媒体库中所有音频文件信息,并在文件增删后及时更新媒体库(转)

转载请注明出处: http://blog.csdn.net/zhaokaiqiang1992 今天一打开博客,发现一位朋友给我发了一封私信,请教下面的问题,所以特写此文章,为其解惑. 从这位朋友的提问中,我们需要了解下面的这些问题,才能给他很好的解答: (1)如何获取手机里所有歌曲的信息? (2)在歌曲文件发生改变,比如增删操作之后,如何及时的更新媒体库,从而获取到最新的歌曲信息? (3)在4.4版本之后,扫描sd卡,更新媒体库的操作发生变化了吗? 下面,我将就以上三个问题,进行解答. (1)如

Android开发本地及网络Mp3音乐播放器(十九)通知媒体库更新刚下载的MP3

实现功能: 通知媒体库更新 纠结了几天,百度到一个方法,可以在安卓4.4,通知媒体库更新. 媒体库更新了,重新打开APP,就可以看到新的歌曲,但这样还是不完善. 后续将博文,将实现 下载音乐成功后,通更新媒体库后,更新本地音乐列表. 因为,没有自己的服务器,所以网络音乐所有相关功能(包含搜索音乐.下载音乐.下载歌词)均无法保证时效性,建议,尽快下载和练习:如果你下载时候,已经因为我采集的服务器更改规则,请给我留言,如果可以解决,我将在有空的时候献上新的源码. 截止到目前的源码下载: http:/

android声音调整源代码分析(转)

From:  http://blog.csdn.NET/bmj/article/details/8796421 加注: Settings.System.SAFE_HEADSET_VOLUME Android调整音量方法有两种,一种是渐进式,即像手动按音量键一样,一步一步增加或减少,另一种是直接设置音量值.        下面先分析第一种渐进式的: [java] view plaincopy AudioManager am = (AudioManager) getSystemService(Con

Android 使用开源库StickyGridHeaders来实现带sections和headers的GridView显示本地图片效果

大家好!过完年回来到现在差不多一个月没写文章了,一是觉得不知道写哪些方面的文章,没有好的题材来写,二是因为自己的一些私事给耽误了,所以过完年的第一篇文章到现在才发表出来,2014年我还是会继续在CSDN上面更新我的博客,欢迎大家关注一下,今天这篇文章主要的是介绍下开源库StickyGridHeaders的使用,StickyGridHeaders是一个自定义GridView带sections和headers的Android库,sections就是GridView item之间的分隔,headers

android获取多媒体库的视频、音频、图片

从媒体库中查找音频.视频.图片文件的相关信息,并获取视频.图片.专辑图片的缩略图 和一些文件操作 package com.uwatch.swconnectservice.util; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import ja