Android 媒体存储服务

本文介绍如何在 Android 中,开发者的 APP 如何使用媒体存储服务(包含MediaScanner、MediaProvider以及媒体信息解析等部分),包括如何把 APP 新增或修改的文件更新到媒体数据库、如何在多媒体应用中隐藏 APP 产生的文件、如何监听媒体数据库的变化等等。
 

简介:
本文介绍如何在 Android 中,开发者的 APP
如何使用媒体存储服务(包含MediaScanner、MediaProvider以及媒体信息解析等部分),包括如何把 APP
新增或修改的文件更新到媒体数据库、如何在多媒体应用中隐藏 APP 产生的文件、如何监听媒体数据库的变化等等。
Android
原生有一套媒体存储服务,进程名是 android.process.media,主要负责把磁盘中的文件信息保存到数据库当中,供其他 APP
使用以及 MTP 模式使用。因此 APP
可以随时快速查询到机器上有多少音乐,音乐的时长、标题、艺术家、专辑封面都可以获取到。下面就介绍我们开发的 APP 如何与这个媒体存储服务打交道。
Note:MTP 模式是 Android 3.0 开始引入的,其数据来源于媒体存储服务。
隐藏多媒体文件
应用场景:APP 产生了图片/音乐/视频类文件,不想让它显示在图库/音乐播放器。市面上有不少游戏,它的图片和音效文件没有做隐藏,出现在用户的图库/音乐播放器当中,引起用户反感。如果用户把它删除了,又可能会影响 APP 正常运行。

方法一:把文件设为隐藏。Linux 里文件前加点就是隐藏,例如“文件A”改成“.文件A”。或者把文件扩展名去掉,这样媒体存储服务扫描时就不会将其当作多媒体文件。
方法二:在文件夹下生成一个名为“.nomedia”的空白文件。这样同一个文件夹下的所有文件都不会被当作多媒体文件。

1.添加/修改多媒体文件
        应用场景:APP 创建了一个新的多媒体文件,或者修改了一个已有多媒体文件。例如 APP 下载了一个音乐文件,需要通知媒体存储服务,用户就能在音乐播放器中看到这个文件。否则,只有下次媒体存储服务开始扫描整个磁盘,才会发现 APP 产生的新文件。

方法一
如果只有一个文件,并且不需要得到结果返回,直接发 Intent 通知媒体存储服务即可。

复制代码 代码如下:
import java.io.File;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;

private static void requestScanFile(Context context, File file) {
    Intent i = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    i.setData(Uri.fromFile(file));
    context.sendBroadcast(i);
}

private static void requestScanFile(Context context, String file) {
    Intent i = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    i.setData(Uri.parse("file://" + Uri.encode(file)));
    context.sendBroadcast(i);
}

Note:
如果使用 Uri.parse() 从文件名生成 Uri,文件名必须要先
Uri.encode(),作用是把保留字符转义。例如文件名若包含“?”,不经过 Uri.encode 转义的话会被当作是查询参数,这样
uri.getPath() 获取的文件路径会丢失“?”之后的部分。

方法二
如果只有一个文件,并且需要文件 uri 结果返回,则使用回调函数。

复制代码 代码如下:
import android.media.MediaScannerConnection;
import android.net.Uri;

private void requestScanFile(Context context, String file) {
    MediaScannerConnection.scanFile(context, new String[] {file},
        null, // mime types,可不指定
        mListener);
}

MediaScannerConnection.OnScanCompletedListener mListener =
        new MediaScannerConnection.OnScanCompletedListener() {
    public void onScanCompleted(String path, Uri uri) {
        // TODO: 获取到该文件在多媒体数据库中的 uri,进行下一步动作
    }
};

Note:还有一种方法,可以先往多媒体数据库插入一条包含文件路径的记录,插入后可以得到其 uri;然后再使用方法一通知媒体存储服务扫描此文件,把文件信息(如专辑名)补充完整。但不推荐使用这种方法,因为获得 uri 时文件信息并不完整。

方法三
如果文件较多,则发 Intent 通知媒体存储服务扫描整个磁盘。这种做法不是特别好,但又没发现其他更好的接口。第三方文件管理如“ES 文件管理器”就是使用这种方法的。

复制代码 代码如下:
import android.content.Context;
import android.content.Intent;
import android.net.Uri;

private static void requestScanDisk(Context context) {
    Intent i = new Intent(Intent.ACTION_MEDIA_MOUNTED);
    String path = Environment.getExternalStorageDirectory().getPath();
    i.setData(Uri.parse("file://" + Uri.encode(path)));
    context.sendBroadcast(i);
}

2.监听数据变化
         应用场景:多媒体数据库有变化,需要刷新 APP 显示界面。比较好理解,磁盘中的多媒体文件有新增、删除或者修改,APP 界面上要实时反应这些变化,刷新显示界面。

方法一
监听媒体存储相关 Intent。接受到 Intent 后,重新查询一次数据库。我们需要注意的 Intent 主要有以下几个:
1、Intent.ACTION_MEDIA_SCANNER_FINISHED: 媒体存储服务扫描整个磁盘完成后会发这个 Intent。可能有新增较多文件或者被删除。
2、Intent.ACTION_MEDIA_SCANNER_SCAN_FILE: 媒体存储服务扫描单个文件。

复制代码 代码如下:
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;

private void registerReceiver(Context context) {
    IntentFilter filter = new IntentFilter(Intent.ACTION_MEDIA_MOUNTED);
    filter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
    filter.addAction(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
    filter.addDataScheme("file");
    context.registerReceiver(mReceiver, filter);
}

private BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        Uri uri = intent.getData();
        if (uri != null && uri.getScheme().equals("file")) {
            Log.v("Receiver", "BroadcastReceiver action = " + action + ", uri = " + uri);
            if (Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)) {
                String filePath = uri.getPath();
                // TODO: filePath 文件已改变,APP 刷新界面
            } else if (Intent.ACTION_MEDIA_SCANNER_FINISHED.equals(action)) {
                // TODO: 整个磁盘扫描完成,APP 刷新界面
            }
        }
    }
};


外,在 Intent.MEDIA_SCANNER_STARTED 和 Intent.ACTION_MEDIA_SCANNER_FINISHED
之间的时间里,媒体存储服务正在扫描文件,数据库会有变化,所以只有在收到 Intent.ACTION_MEDIA_SCANNER_FINISHED
之后查询的结果才是准确的。如果要检测媒体存储服务是否在扫描可以用以下方法:

复制代码 代码如下:
import android.content.ContentResolver;
import android.database.Cursor;
import android.os.Environment;
import android.provider.MediaStore;

private static boolean isMediaScannerScanning(ContentResolver cr) {
    Cursor cursor = null;
    try {
        cursor = cr.query(MediaStore.getMediaScannerUri(), new String[] {
            MediaStore.MEDIA_SCANNER_VOLUME}, null, null, null);

if (cursor != null && cursor.getCount() > 0) {
            cursor.moveToFirst();
            return "external".equals(cursor.getString(0));
        }
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }

return false;
}

Note:APP 可能还需要监听存储设备的变化,例如 SD 卡拔出、磁盘挂载出去(USB 大容量存储模式)等等,这些情况下可能要把文件显示界面清空,或者退出程序。各个手机对于每个 Intent 定义可能略有不同,但基本上是以下几个:
1、Intent.ACTION_MEDIA_EJECT: 存储设备正常移除,例如在设置里卸载存储器。
2、Intent.ACTION_MEDIA_UNMOUNTED: 存储设备正常卸载,通常与 EJECT 先后出现。
3、Intent.ACTION_MEDIA_BAD_REMOVAL: 非正常移除存储设备,例如硬插拔 SD 卡。

方法二
监听数据库变化。如果需要在数据库发生变化时能实时接收到通知,可以使用 ContentObserver。

复制代码 代码如下:
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;

private ContentObserver mContentObserver = new ContentObserver(null) {
    @Override
    public void onChange(boolean selfChange) { // 往后兼容
        onChange(selfChange, null);
    }

public void onChange(boolean selfChange, Uri uri) {
        // TODO: 数据已改变,APP 重新查询数据库并刷新界面
    }
};

private void setupCursor(Context context, Cursor c) {
    c.unregisterContentObserver(mContentObserver); // c 为需要显示的数据
}

此外,使用 CursorAdapter 和显示的 ListView 绑定,也可以达到同样目的。当 Cursor 内容发现变化,ListView 也会相应自动刷新。

Android 媒体存储服务(一)的更多相关文章

  1. Android 媒体存储服务(二)

    Android 媒体存储服务 简介: 本文是<深入Android媒体存储服务>系列第二篇,简要介绍媒体存储服务扫描文件的流程.文中介绍的是 Android 4.2. Android 有一套 ...

  2. 专题合集:深入Android媒体存储服务

    Android 有一套媒体存储服务,进程名是 android.process.media,主要负责把磁盘中的文件信息保存到数据库当中,供其他 APP 使用以及 MTP 模式使用.这里包含了数据库管理. ...

  3. 深入Android媒体存储服务(二):磁盘扫描流程

    简介: 本文是<深入Android媒体存储服务>系列第二篇,简要介绍媒体存储服务扫描文件的流程.文中介绍的是 Android 4.2. Android 有一套媒体存储服务,进程名是 and ...

  4. 深入Android媒体存储服务(一):APP与媒体存储服务的交互

    简介: 本文介绍如何在 Android 中,开发者的 APP 如何使用媒体存储服务(包含MediaScanner.MediaProvider以及媒体信息解析等部分),包括如何把 APP 新增或修改的文 ...

  5. Linux最佳的云存储服务分析

    什么样的云服务才适合作为 Linux 下的存储服务?兄弟连www.itxdl.cn来帮大家分析一下! 大量的免费空间.毕竟,个人用户无法支付每月的巨额款项. 原生的 Linux 客户端.以便你能够方便 ...

  6. Mosquitto搭建Android推送服务(一)MQTT简介

    总体概要: MQTT系列文章分为4部分 1.MQTT简介 2.mosquitto服务器搭建 3.编写Mosquitto的可视化工具 4.使用Mosquitto完成Android推送服务 文章钢要: 对 ...

  7. 阿里云的NoSQL存储服务OTS的应用分析

    这篇文章主要介绍了阿里云的NoSQL存储服务OTS的应用分析,OTS作为阿里巴巴开发的NoSQL存储技术服务现已面向用户商业化,需要的朋友可以参考下. 1. 概要  OTS是构建在阿里云飞天分布式系统 ...

  8. android媒体--图库与API层MediaPlayer的交互

    众所周知一个媒体播放器新建的几个步骤: Mediaplayer mp = new MediaPlayer(0 mp.setDatasource(xxx); mp.setDispalyer(xxx); ...

  9. Android - 数据存储 -存储文件

    Android使用的文件系统和其他平台的基本磁盘的文件系统很相似.这里将要介绍如何使用File API在Android文件系统中读写文件. File对象适合按顺序读写大量的数据.例如,适合图片文件或者 ...

随机推荐

  1. 让ie6对png透明图片支持起来

    [声明:此文仅是对低版本ie使用透明图片的一个研究,当时出于工作要求,所以花费了一番心思在兼容旧版本ie上,现在对ie8都是做降级处理了.不培养用户坏习惯.引导用户跟随潮流体验新技术应是我们前端开发者 ...

  2. IOS9 Swift

    Swift 2.0发布:即将开源,支持Linux [日期:2015-06-09] 来源:infoq  作者:Linux [字体:大 中 小]     在6月9日凌晨举行的WWDC 2015全球开发者大 ...

  3. Shell study note

    td p { margin-bottom: 0in } p { margin-bottom: 0.1in; line-height: 120% } a:link { } 5.1 printenv vi ...

  4. Game02 最新版本2.0.0

    Game02发布转站于此,欢迎大家下载.... 简介: Game02是由ImGenius开发的一款即时在线小游戏.玩法简单,易于上手,主要靠技术,不会出现不要脸的充值等内容. 操控方法:W和A左右移动 ...

  5. 【接口】【USB】1.学习笔记

    1.USB的优点: 可以热插拔,即插上后可以自动识别: 系统总线供电,USB共有四根线,一根电源线,一根地线,一根D+线,一根D-线,D+和D-线是差分输入线: 可以支持多种设备,且扩展容易,通过HU ...

  6. node模块系统常用命令

    node模块系统常用命令 命令 示例 备注 安装模块 npm install commander 最新版本 npm install commander@1.0.0 指定版本 npm install c ...

  7. 支付宝APP支付后台参数生成Java版(一)

    一.支付参数组装: String[] parameters={ "service=\"mobile.securitypay.pay\"",//固定值 " ...

  8. USB协议规范学习(一)

    什么是USB OHCI规范? OHCI(Open HCI)是目前使用比较广泛的三种USB主机控制器规范之一.USB体系结构是由四个主要部分组成:客户软件/USB驱动,主机控制器驱动(HCD),主机控制 ...

  9. knockout应用开发指南(完整版)

    http://www.cnblogs.com/TomXu/archive/2011/11/21/2257154.html

  10. Python开发入门与实战16-APACHE部署

    16. Windows平台apache部署 本章节我们简要的描述一下如何在windows平台部署apache的django站点. Python Django 项目部署发布到windows apache ...