`
dengbaoleng
  • 浏览: 1128597 次
文章分类
社区版块
存档分类
最新评论

Android开发——MediaProvider源码分析(二)

 
阅读更多

在上一篇文章中说到系统当接收到扫描请求广播的时候就会调用scan或者scanFile去扫描手机(手机内存和sdcard)中的媒体文件。这两个方法都是启动MediaScannerService这个服务来完成扫描任务的。接下来我们来看看MediaScannerService是怎么工作的……

4.MediaScannerService(MSS)

MSS实现了Runnable,所以必然的需要实现run方法了,代码如下:

   1: public void run()
   2:    {
   3:        // reduce priority below other background threads to avoid interfering
   4:        // with other services at boot time.
   5:        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND +
   6:                Process.THREAD_PRIORITY_LESS_FAVORABLE);
   7:        Looper.prepare();
   8: 
   9:        mServiceLooper = Looper.myLooper();
  10:        mServiceHandler = new ServiceHandler();
  11: 
  12:        Looper.loop();
  13:    }
在run方法中设置了线程的优先级,优先级比较低,主要为了避免跟其他服务抢夺资源。还有就是利用looper对ServiceHandler的消息进行循环控制。

接着看一下ServiceHandler的实现代码:

   1: private final class ServiceHandler extends Handler
   2:    {
   3:        @Override
   4:        public void handleMessage(Message msg)
   5:        {
   6:            Bundle arguments = (Bundle) msg.obj;
   7:            //获取文件路径
   8:            String filePath = arguments.getString("filepath");
   9:            
  10:            try {
  11:                if (filePath != null) {
  12:                     //文件路径不为空,则调用扫面当个文件的方法
  13:                    IBinder binder = arguments.getIBinder("listener");
  14:                    IMediaScannerListener listener = 
  15:                            (binder == null ? null : IMediaScannerListener.Stub.asInterface(binder));
  16:                    Uri uri = scanFile(filePath, arguments.getString("mimetype"));//扫描单个文件
  17:                    if (listener != null) {
  18:                         //执行扫描完成方法
  19:                        listener.scanCompleted(filePath, uri);
  20:                    }
  21:                } else {
  22:                     //如果文件路径为空,则获取扫面手机内存或者sdcard
  23:                    String volume = arguments.getString("volume");
  24:                    String[] directories = null;
  25:                    //内置卡
  26:                    if (MediaProvider.INTERNAL_VOLUME.equals(volume)) {
  27:                        // scan internal media storage
  28:                        directories = new String[] {
  29:                                Environment.getRootDirectory() + "/media",
  30:                        };
  31:                    }//外置卡
  32:                    else if (MediaProvider.EXTERNAL_VOLUME.equals(volume)) {
  33:                        // scan external storage
  34:                        directories = new String[] {
  35:                                Environment.getExternalStorageDirectory().getPath(),
  36:                                };
  37:                    }
  38:                    
  39:                    if (directories != null) {
  40:                        if (Config.LOGD) Log.d(TAG, "start scanning volume " + volume);
  41:                         //扫描
  42:                        scan(directories, volume);
  43:                        if (Config.LOGD) Log.d(TAG, "done scanning volume " + volume);
  44:                    }
  45:                }
  46:            } catch (Exception e) {
  47:                Log.e(TAG, "Exception in handleMessage", e);
  48:            }
  49: 
  50:            stopSelf(msg.arg1);
  51:        }
  52:    };

在ServiceHandler中主要根据相关参数来调用不同的扫描方法。大笑

那是在哪里调用ServiceHandler发送消息的呢?请看如下代码:

   1: @Override
   2: public void onCreate() {
   3:     PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
   4:     mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
   5:     //启用新线程,这样就可以避免阻塞,执行run,初始化成员变量loop和handler
   6:     Thread thr = new Thread(null, this, "MediaScannerService");
   7:     thr.start();
   8: }
   9: 
  10: @Override
  11: public int onStartCommand(Intent intent, int flags, int startId) {
  12:     while (mServiceHandler == null) {
  13:         synchronized (this) {
  14:             try {
  15:                 wait(100);
  16:             } catch (InterruptedException e) {
  17:             }
  18:         }
  19:     }
  20: 
  21:     if (intent == null) {
  22:         Log.e(TAG, "Intent is null in onStartCommand: ", new NullPointerException());
  23:         return Service.START_NOT_STICKY;
  24:     }
  25: 
  26:     Message msg = mServiceHandler.obtainMessage();
  27:     msg.arg1 = startId;
  28:     msg.obj = intent.getExtras();
  29:     //ServiceHandler发送消息
  30:     mServiceHandler.sendMessage(msg);
  31: 
  32:     // Try again later if we are killed before we can finish scanning.
  33:     return Service.START_REDELIVER_INTENT;
  34: }
  35: 
  36: @Override
  37: public void onDestroy() {
  38:     // Make sure thread has started before telling it to quit.
  39:     while (mServiceLooper == null) {
  40:         synchronized (this) {
  41:             try {
  42:                 wait(100);
  43:             } catch (InterruptedException e) {
  44:             }
  45:         }
  46:     }
  47:     mServiceLooper.quit();
  48: }
以上三个方法是属于Service的生命周期的。当我们调用startService的时候,如果对应的Service还未创建就会调用onCreate方法===方法。每次startService的时候就调用onStartCommand,所以ServiceHandler就在此发送消息了。

最后,稍微看一下MSS里面扫描方面。主要是调用MediaScanner对媒体文件进行扫描分析的。至于MediaScanner的实现以后在分析。

   1: private void openDatabase(String volumeName) {
   2:        try {
   3:            ContentValues values = new ContentValues();
   4:            values.put("name", volumeName);
   5:            getContentResolver().insert(Uri.parse("content://media/"), values);
   6:        } catch (IllegalArgumentException ex) {
   7:            Log.w(TAG, "failed to open media database");
   8:        }         
   9:    }
  10: 
  11:    private void closeDatabase(String volumeName) {
  12:        try {
  13:            getContentResolver().delete(
  14:                    Uri.parse("content://media/" + volumeName), null, null);
  15:        } catch (Exception e) {
  16:            Log.w(TAG, "failed to close media database " + volumeName + " exception: " + e);
  17:        }
  18:    }
  19: //创建扫描器
  20:    private MediaScanner createMediaScanner() {
  21:        MediaScanner scanner = new MediaScanner(this);
  22:        Locale locale = getResources().getConfiguration().locale;
  23:        if (locale != null) {
  24:            String language = locale.getLanguage();
  25:            String country = locale.getCountry();
  26:            String localeString = null;
  27:            if (language != null) {
  28:                if (country != null) {
  29:                    scanner.setLocale(language + "_" + country);
  30:                } else {
  31:                    scanner.setLocale(language);
  32:                }
  33:            }    
  34:        }
  35:        
  36:        return scanner;
  37:    }
  38: //扫描目录
  39:    private void scan(String[] directories, String volumeName) {
  40:        // don't sleep while scanning
  41:        mWakeLock.acquire();
  42: 
  43:        ContentValues values = new ContentValues();
  44:        values.put(MediaStore.MEDIA_SCANNER_VOLUME, volumeName);
  45:        Uri scanUri = getContentResolver().insert(MediaStore.getMediaScannerUri(), values);
  46: 
  47:        Uri uri = Uri.parse("file://" + directories[0]);
  48:        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_STARTED, uri));
  49:        
  50:        try {
  51:            if (volumeName.equals(MediaProvider.EXTERNAL_VOLUME)) {
  52:                 openDatabase(volumeName);    
  53:            }
  54: 
  55:            MediaScanner scanner = createMediaScanner();
  56:            scanner.scanDirectories(directories, volumeName);
  57:        } catch (Exception e) {
  58:            Log.e(TAG, "exception in MediaScanner.scan()", e); 
  59:        }
  60: 
  61:        getContentResolver().delete(scanUri, null, null);
  62: 
  63:        sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_FINISHED, uri));
  64:        mWakeLock.release();
  65:    }
  66: //扫描文件
  67: private Uri scanFile(String path, String mimeType) {
  68:         String volumeName = MediaProvider.INTERNAL_VOLUME;
  69:         String externalStoragePath = Environment.getExternalStorageDirectory().getPath();
  70: 
  71:         if (path.startsWith(externalStoragePath)) {
  72:             volumeName = MediaProvider.EXTERNAL_VOLUME;
  73:             openDatabase(volumeName);
  74:         }
  75:         MediaScanner scanner = createMediaScanner();
  76:         //扫描单个文件
  77:         return scanner.scanSingleFile(path, volumeName, mimeType);
  78:     }

在MediaProvider中还有一个类:MediaThumbRequest,用来创建预览图的,比如视频的预览图,图片的预览图,音频的专辑图片…这些图片的信息也是保存在数据库的,有兴趣的同学可以自己打开数据库看看里面的表。如下图:

media_db_tables

啰哩啰唆的写了两篇文章,希望对大家有所帮助。

其中应该有不少是错误的观点,望大家指正。

----------------------END------------------------------


分享到:
评论

相关推荐

    android MediaProvider和MediaScanner详解

    关于Android媒体存储和手机数据库扫描流程以及优化的部分代码贴图

    MediaProvider

    自己总结学习MediaProvider的一些重要函数,为了以后学习方便

    Android4.4下MediaProvider无法向外置SD卡中文件写数据的解决方法

    主要介绍了Android4.4下MediaProvider无法向外置SD卡中文件写数据的解决方法,实例分析了Android4.4下针对读写限制的修改技巧,具有一定参考借鉴价值,需要的朋友可以参考下

    media provider源码

    media provider源码,原生系统代码,通过ContentProvider实现了数据管理功能。

    apk文件 MediaProvider(电视直播视频)

    apk文件 MediaProvider(电视直播视频)apk文件 MediaProvider(电视直播视频)apk文件 MediaProvider(电视直播视频)apk文件 MediaProvider(电视直播视频)apk文件 MediaProvider(电视直播视频)apk文件 ...

    Mediaprovider简介

    Mediaprovider的简单说明。 MediaProvider包括五个类: com.android.providers.media.MediaProvider com.android.providers.media.MediaScannerCursor com.android.providers.media.MediaScannerReceiver ...

    Android 获取音乐文件的信息

    Android 获取音乐文件的信息 Android系统提供了MediaScanner,MediaProvider,MediaStore等接口,并且提供了一套数据库表格,通过Content Provider的方式提供给用户。

    《深入理解Android》卷Ⅱ

    第1章 搭建Android源码工作环境 1.1 Android系统架构 1.2 搭建开发环境 1.2.1 下载源码 1.2.2 编译源码 1.2.3 利用Eclipse调试system_process 1.3 本章小结 第2章 深入理解Java Binder和MessageQueue 2.1 ...

    Android 4.4及以上版本写入外置SD卡问题

    由于现在大多数手机都是带有内存的,原本获取外置SD卡路径的方法Environment.getExternalStorageDirectory() 获取得到的是手机自身内存的根目录。那么我们要怎么来获取到外置SD卡的路径,首先需要A判断是否挂载了sdk...

    深入理解android 卷II 第7章

    深入理解android 卷II 第7章 ContentProvider深入理解

    Android Music

    Android源码的Music应用相关代码不算多,packages/apps/Music是关于UI界面的,\packages\providers\MediaProvider关于数据库的,数据库文件放在data/data/com.android.providers.media, 这里面有两个或更多个.db文件...

    Android中通过MediaStore获取音乐文件信息方法

    Android系统提供了MediaScanner,MediaProvider,MediaStore等接口,并且提供了一套数据库表格,通过Content Provider的方式提供给用户。当手机开机或者有SD卡插拔等事件发生时,系统将会自动扫描SD卡和手机内存上的...

    Android APP与媒体存储服务的交互

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

    Android中扫描多媒体文件操作详解

    这篇文章从系统源代码分析,讲述如何将程序创建的多媒体文件加入系统的媒体库,如何从媒体库删除,以及大多数程序开发者经常遇到的无法添加到媒体库的问题等。本人将通过对源代码的分析,一一解释这些问题。 Android...

    SEAndroid问题快速分析

    快速定位、分析和解决Android系统SELinux相关的权限问题。

    Android编程获取图片和视频缩略图的方法

    从Android 2.2开始系统新增了一个缩略图ThumbnailUtils类,位于framework的android.media.ThumbnailUtils位 置,可以帮助我们从mediaprovider中获取系统中的视频或图片文件的缩略图,该类提供了三种静态方法可以直接...

    Android获取相册图片-实现选择相册图片功能

    下面我们来看一下媒体库的内容提供者的URI(源码地址) 媒体库Uri 图片MediaStore.Images.Media.EXTERNAL_CONTENT_URI 音频MediaStore.Audio.Media.EXTERNAL_CONTENT_URI 视频MediaStore.Video.Media.EXTERNAL_CONTENT...

    完美解决android M上锁屏情况下,禁止pc通过MTP访问手机存储单元

    1、问题解决主要文件:/m8976/packages/providers/MediaProvider/src/com/android/providers/media/MtpService.java 需要在MtpService.java中updateDisabledStateLocked 方法添加锁屏情况限制: final ...

Global site tag (gtag.js) - Google Analytics