注:本文转载于:http://yueguc.iteye.com/blog/836964 http://www.61ic.com/Mobile/Android/201102/29418.html

下面是系统


MediaScannerReceiver会在任何的 ACTION_BOOT_COMPLETED, ACTION_MEDIA_MOUNTED ACTION_MEDIA_SCANNER_SCAN_FILE意图( intent )发出的时候启动。因为解析媒体文件 的元数据 或许会需要很长时间 ,所以MediaScannerReceiver 会启动MediaScannerService


MediaScannerService 调用一个公用类MediaScanner 去处理真正的工作。MediaScannerReceiver 维持两种扫描目录:一种是内部卷(internal volume )指向$(ANDROID_ROOT)/media. 另一种是外部卷(external volume )指向$(EXTERNAL_STORAGE).

扫描和解析工作位于 JAVA 层和 C++ 层。 JAVA 层是启动器。 MediaScanner扫描所有目录,如下步骤:


1.JAVA 层初始化


在这一步骤中,它会根据目录是在内部卷还是外部卷打开不同的数据库


2.Java 层预扫描


首先清除文件和播放 列表的缓存条目。然后根据MediaProvider 返回的请求结果生成新文件和播放列表缓存条目。


3.C++ 层处理目录


列举出所有文件和特定的所有子目录(如果子目录包含一个.nomedia 隐藏文件,则不会被列举出来。)。被列举的文件是根据文件扩展来判断文件是否被支持。如果支持这种文件扩展,C++ 层就会回调到 JAVA 层扫描文件。这种扩展就会被扫描到 MediaFile.java 中列出。下面是支持的文件扩展列表。


/* Audio */
addFileType("MP3", FILE_TYPE_MP3, "audio/mpeg");
addFileType("M4A", FILE_TYPE_M4A, "audio/mp4");
addFileType("WAV", FILE_TYPE_WAV, "audio/x-wav");
addFileType("AMR", FILE_TYPE_AMR, "audio/amr");
addFileType("AWB", FILE_TYPE_AWB, "audio/amr-wb");
addFileType("WMA", FILE_TYPE_WMA, "audio/x-ms-wma");
addFileType("OGG", FILE_TYPE_OGG, "application/ogg");
addFileType("MID", FILE_TYPE_MID, "audio/midi");
addFileType("XMF", FILE_TYPE_MID, "audio/midi");
addFileType("RTTTL", FILE_TYPE_MID, "audio/midi");
addFileType("SMF", FILE_TYPE_SMF, "audio/sp-midi");
addFileType("IMY", FILE_TYPE_IMY, "audio/imelody");

/* Video */
addFileType("MP4", FILE_TYPE_MP4, "video/mp4");
addFileType("M4V", FILE_TYPE_M4V, "video/mp4");
addFileType("3GP", FILE_TYPE_3GPP, "video/3gpp");
addFileType("3GPP", FILE_TYPE_3GPP, "video/3gpp");
addFileType("3G2", FILE_TYPE_3GPP2, "video/3gpp2");
addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2");
addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv");

/* Image */
addFileType("JPG", FILE_TYPE_JPEG, "image/jpeg");
addFileType("JPEG", FILE_TYPE_JPEG, "image/jpeg");
addFileType("GIF", FILE_TYPE_GIF, "image/gif");
addFileType("PNG", FILE_TYPE_PNG, "image/png");
addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp");
addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp");

/* Audio Play List */
addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl");
addFileType("PLS", FILE_TYPE_PLS, "audio/x-scpls");
addFileType("WPL", FILE_TYPE_WPL, "application/vnd.ms-wpl");


4.Java层扫描文件




a Java 层开始文件




首先它忽略一些 MacOS Windows Media Player特殊的文件。然后它会查看被扫描的文件是否已经存在于缓存条目中,如果存在,它会检查文件上次修改的时间是否改变。最后它返回该文件是否需要进一步处理的结果。如果不需要,接下来的两步不会执行。




b)C++层扫描文件




不是所有的文件都需要交给 C++ 层解析成元数据。只有下面的文件类型会被解析,注意,这里不处理 image 文件。




  1. if (mFileType == MediaFile.FILE_TYPE_MP3 ||
  2. mFileType == MediaFile.FILE_TYPE_MP4 ||
  3. mFileType == MediaFile.FILE_TYPE_M4A ||
  4. mFileType == MediaFile.FILE_TYPE_3GPP ||
  5. mFileType == MediaFile.FILE_TYPE_3GPP2 ||
  6. mFileType == MediaFile.FILE_TYPE_OGG ||
  7. mFileType == MediaFile.FILE_TYPE_MID ||
  8. mFileType == MediaFile.FILE_TYPE_WMA) {


  9. ……



复制代码


对于被解析的元数据信息, C++ 层会回调到 JAVA层的 handleStringTag Java 层会记录它的name/value 信息。



c)Java层结束文件



最后根据上一步解析出的值, Java 层会更新相应的 MeidaProvider产生的数据库表。



5.Java层发送扫描



到目前为止,所有文件已经被扫描,它最后会检查文件和播放列表缓存条目,看是否所有项仍然存在于文件系统。如果有空条目,则会从数据库中删除。这样它能够保持数据库和文件系统的一致性。



其他的应用 程序 通过接收 MediaScannerService发出的 ACTION_MEDIA_SCANNER_STARTED ACTION_MEDIA_SCANNER_FINISHED意图能够知道什么时候扫描操作开始和结束。

MediaScanner

之所以拿MediaScanner开刀 因为想借用系统的Media Scan 工具 通过Intent直接调用系统的

[步骤]

1. 下载并安装Git 过程略 网络上很多

2. 得到该功能的模块地址并使用Git下载之 地址:git://android.git.kernel.org/platform/packages/providers/MediaProvider.git

3. 分析源代码:

- AndroidManifest.xml : 各组件属性描述文件

- MediaProvider : extends ContentProvider 使用SQLiteDatabase 保存查询数据 action="content://media"

- MediaScannerCursor.java

- MediaScannerReceiver : extends BroadcastReceiver 用于接收指定Broadcast: BOOT_COMPLETED MEDIA_MOUNTED MEDIA_SCANNER_SCAN_FILE 并启动 MediaScannerService 开始扫描

- MediaScannerService : extends Service 执行具体的扫描工作

- MediaThumbRequest

4. 鉴于 并不打算自行实现多媒体扫描 因此 此次重点研究对象:MediaScannerReceiver

5. MediaScannerReceiver 代码

Java代码
  1. public class MediaScannerReceiverextends BroadcastReceiver
  2. {
  3. private final staticString TAG = "MediaScannerReceiver" ;
  4. @Override
  5. public void onReceive(Context context, Intent intent) {
  6. String action = intent.getAction();
  7. Uri uri = intent.getData();
  8. String externalStoragePath = Environment.getExternalStorageDirectory().getPath();
  9. if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
  10. // scan internal storage
  11. scan(context, MediaProvider.INTERNAL_VOLUME);
  12. } else {
  13. if (uri.getScheme().equals( "file" )) {
  14. // handle intents related to external storage
  15. String path = uri.getPath();
  16. if (action.equals(Intent.ACTION_MEDIA_MOUNTED) &&
  17. externalStoragePath.equals(path)) {
  18. scan(context, MediaProvider.EXTERNAL_VOLUME);
  19. } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) &&
  20. path != null && path.startsWith(externalStoragePath +"/" )) {
  21. scanFile(context, path);
  22. }
  23. }
  24. }
  25. }
  26. private void scan(Context context, String volume) {
  27. Bundle args = new Bundle();
  28. args.putString("volume" , volume);
  29. context.startService(
  30. new Intent(context, MediaScannerService.class ).putExtras(args));
  31. }
  32. private void scanFile(Context context, String path) {
  33. Bundle args = new Bundle();
  34. args.putString("filepath" , path);
  35. context.startService(
  36. new Intent(context, MediaScannerService.class ).putExtras(args));
  37. }
  38. }
Java代码
  1. public class MediaScannerReceiverextends BroadcastReceiver
  2. {
  3. private final static String TAG ="MediaScannerReceiver";
  4. @Override
  5. public void onReceive(Context context, Intent intent) {
  6. String action = intent.getAction();
  7. Uri uri = intent.getData();
  8. String externalStoragePath = Environment.getExternalStorageDirectory().getPath();
  9. if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
  10. // scan internal storage
  11. scan(context, MediaProvider.INTERNAL_VOLUME);
  12. } else {
  13. if (uri.getScheme().equals("file")) {
  14. // handle intents related to external storage
  15. String path = uri.getPath();
  16. if (action.equals(Intent.ACTION_MEDIA_MOUNTED) &&
  17. externalStoragePath.equals(path)) {
  18. scan(context, MediaProvider.EXTERNAL_VOLUME);
  19. } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) &&
  20. path != null && path.startsWith(externalStoragePath +"/")) {
  21. scanFile(context, path);
  22. }
  23. }
  24. }
  25. }
  26. private void scan(Context context, String volume) {
  27. Bundle args = new Bundle();
  28. args.putString("volume", volume);
  29. context.startService(
  30. new Intent(context, MediaScannerService.class).putExtras(args));
  31. }
  32. private void scanFile(Context context, String path) {
  33. Bundle args = new Bundle();
  34. args.putString("filepath", path);
  35. context.startService(
  36. new Intent(context, MediaScannerService.class).putExtras(args));
  37. }
  38. }
public class MediaScannerReceiver extends BroadcastReceiver{    private final static String TAG = "MediaScannerReceiver";    @Override    public void onReceive(Context context, Intent intent) {        String action = intent.getAction();        Uri uri = intent.getData();        String externalStoragePath = Environment.getExternalStorageDirectory().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) &&                         externalStoragePath.equals(path)) {                    scan(context, MediaProvider.EXTERNAL_VOLUME);                } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) &&                        path != null && path.startsWith(externalStoragePath + "/")) {                    scanFile(context, path);                }            }        }    }    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();        args.putString("filepath", path);        context.startService(                new Intent(context, MediaScannerService.class).putExtras(args));    }    }

6. 根据以上代码得知:

- 当系统启动完毕 会扫描一次

- 当 ACTION_MEDIA_MOUNTED ACTION_MEDIA_SCANNER_SCAN_FILE 也会扫描

7. 如何调用系统MediaScanner 进行扫描

- 通过 Intent.ACTION_MEDIA_MOUNTED 进行全扫描

Java代码
  1. public void allScan(){
  2. sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"
  3. + Environment.getExternalStorageDirectory())));
  4. }
Java代码
  1. public void allScan(){
  2. sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"
  3. + Environment.getExternalStorageDirectory())));
  4. }
public void allScan(){    sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"                + Environment.getExternalStorageDirectory())));    }

- 通过 Intent.ACTION_MEDIA_SCANNER_SCAN_FILE 扫描某个文件

Java代码
  1. public void fileScan(String fName){
  2. Uri data = Uri.parse("file:///" +fName);
  3. sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data));
  4. }
Java代码
  1. public void fileScan(String fName){
  2. Uri data = Uri.parse("file:///"+fName);
  3. sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data));
  4. }
public void fileScan(String fName){    Uri data = Uri.parse("file:///"+fName);    sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data));    }

补充: 上述方法是不支持对文件夹的 即:Uri data 必须是 文件的Uri 如果是文件夹的 其不会起作用的 切记!

- 如何扫描某文件夹下所有文件 难道就不可以么? 当然不 借助于Intent.ACTION_MEDIA_SCANNER_SCAN_FILE

我们可以这么做: 取出该文件夹下的所有子文件 如其是文件且类型符合条件 就取出该文件目录 以 Intent.ACTION_MEDIA_SCANNER_SCAN_FILE方式发送至MediaScannerReceiver 若其为文件夹 则迭代查询之 故实现为:

Java代码
  1. public void fileScan(String file){
  2. Uri data = Uri.parse("file://" +file);
  3. Log.d("TAG" , "file:" +file);
  4. sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data));
  5. }
  6. public void folderScan(String path){
  7. File file = new File(path);
  8. if (file.isDirectory()){
  9. File[] array = file.listFiles();
  10. for ( int i=0 ;i<array.length;i++){
  11. File f = array[i];
  12. if (f.isFile()){ //FILE TYPE
  13. String name = f.getName();
  14. if (name.contains( ".mp3" )){
  15. fileScan(f.getAbsolutePath());
  16. }
  17. }
  18. else { //FOLDER TYPE
  19. folderScan(f.getAbsolutePath());
  20. }
  21. }
  22. }
  23. }
Java代码
  1. public void fileScan(String file){
  2. Uri data = Uri.parse("file://"+file);
  3. Log.d("TAG","file:"+file);
  4. sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data));
  5. }
  6. public void folderScan(String path){
  7. File file = new File(path);
  8. if(file.isDirectory()){
  9. File[] array = file.listFiles();
  10. for(int i=0;i<array.length;i++){
  11. File f = array[i];
  12. if(f.isFile()){//FILE TYPE
  13. String name = f.getName();
  14. if(name.contains(".mp3")){
  15. fileScan(f.getAbsolutePath());
  16. }
  17. }
  18. else {//FOLDER TYPE
  19. folderScan(f.getAbsolutePath());
  20. }
  21. }
  22. }
  23. }
public void fileScan(String file){    Uri data = Uri.parse("file://"+file);        Log.d("TAG","file:"+file);    sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data));    }        public void folderScan(String path){    File file = new File(path);        if(file.isDirectory()){    File[] array = file.listFiles();        for(int i=0;i<array.length;i++){    File f = array[i];        if(f.isFile()){//FILE TYPE    String name = f.getName();        if(name.contains(".mp3")){    fileScan(f.getAbsolutePath());    }    }    else {//FOLDER TYPE    folderScan(f.getAbsolutePath());    }    }    }    }

8. 鉴于多数人并不关心其原理 仅关系如何使用 故 总结如下:

- 扫描全部 我猜测其在效率方面可能有点副作用

Java代码
  1. public void systemScan(){
  2. sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"
  3. + Environment.getExternalStorageDirectory())));
  4. }
Java代码
  1. public void systemScan(){
  2. sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"
  3. + Environment.getExternalStorageDirectory())));
  4. }
public void systemScan(){    sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://"                + Environment.getExternalStorageDirectory())));    }

- 扫描某个文件 参数:填入该文件的路径

Java代码
  1. public void fileScan(String file){
  2. Uri data = Uri.parse("file://" +file);
  3. sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data));
  4. }
Java代码
  1. public void fileScan(String file){
  2. Uri data = Uri.parse("file://"+file);
  3. sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data));
  4. }
public void fileScan(String file){    Uri data = Uri.parse("file://"+file);        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data));    }

- 扫描文件夹 参数:填入该文件夹路径

Java代码
  1. public void fileScan(String file){
  2. Uri data = Uri.parse("file://" +file);
  3. sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data));
  4. }
  5. public void folderScan(String path){
  6. File file = new File(path);
  7. if (file.isDirectory()){
  8. File[] array = file.listFiles();
  9. for ( int i=0 ;i<array.length;i++){
  10. File f = array[i];
  11. if (f.isFile()){ //FILE TYPE
  12. String name = f.getName();
  13. if (name.contains( ".mp3" )){
  14. fileScan(f.getAbsolutePath());
  15. }
  16. }
  17. else { //FOLDER TYPE
  18. folderScan(f.getAbsolutePath());
  19. }
  20. }
  21. }
  22. }
Java代码
  1. public void fileScan(String file){
  2. Uri data = Uri.parse("file://"+file);
  3. sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data));
  4. }
  5. public void folderScan(String path){
  6. File file = new File(path);
  7. if(file.isDirectory()){
  8. File[] array = file.listFiles();
  9. for(int i=0;i<array.length;i++){
  10. File f = array[i];
  11. if(f.isFile()){//FILE TYPE
  12. String name = f.getName();
  13. if(name.contains(".mp3")){
  14. fileScan(f.getAbsolutePath());
  15. }
  16. }
  17. else {//FOLDER TYPE
  18. folderScan(f.getAbsolutePath());
  19. }
  20. }
  21. }
  22. }
public void fileScan(String file){    Uri data = Uri.parse("file://"+file);        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, data));    }        public void folderScan(String path){    File file = new File(path);        if(file.isDirectory()){    File[] array = file.listFiles();        for(int i=0;i<array.length;i++){    File f = array[i];        if(f.isFile()){//FILE TYPE    String name = f.getName();        if(name.contains(".mp3")){    fileScan(f.getAbsolutePath());    }    }    else {//FOLDER TYPE    folderScan(f.getAbsolutePath());    }    }    }    }

终于结束了 看似简单的东西 研究起来 还是挺复杂的!

更多相关文章

  1. android典型代码系列(一)------android调试
  2. android灵活布局
  3. Android应用开发相关下载资源(2016/07/24更新)
  4. android (22)
  5. android intent
  6. Android(安卓)Ble
  7. Android(安卓)图片缩放
  8. Android(安卓)usb网络共享开关
  9. Android笔记

随机推荐

  1. android 模拟物理按键
  2. Android 拨打webView 里面的电话号码的方
  3. VLC for Android on Linux
  4. 《一》Android(安卓)数据库 SQlite SQLit
  5. Android Toast 保持一直显示。
  6. Android(安卓)Mini音乐播放器[简单版本]
  7. how the Android IPC system works
  8. 你真的懂Handler吗?Handler问答
  9. Android机器人电池插件源码
  10. android startService小例子