• Android开发——MediaProvider源码分析(1)


    转载请注明出处。

    --------------START------------

    MediaProvider包括五个类:

    • com.android.providers.media.MediaProvider
    • com.android.providers.media.MediaScannerCursor
    • com.android.providers.media.MediaScannerReceiver
    • com.android.providers.media.MediaScannerService
    • com.android.providers.media.MediaThumbRequest

    1.MediaProvider

    此类继承ContentProvider,实现一个内容提供者。主要用于创建媒体库的数据库表。有自己创建过ContentProvider的同学相信都比较清楚的。

    特别说明一下在MediaProvider中有个广播接收者,代码如下:

       1: private BroadcastReceiver mUnmountReceiver = new BroadcastReceiver() {
       2:         @Override
       3:         public void onReceive(Context context, Intent intent) {
       4:             if (intent.getAction().equals(Intent.ACTION_MEDIA_EJECT)) {
       5:                 // Remove the external volume and then notify all cursors backed by
       6:                 // data on that volume
       7:                 detachVolume(Uri.parse("content://media/external"));
       8:                 sFolderArtMap.clear();
       9:                 MiniThumbFile.reset();
      10:             }
      11:         }
      12:     };

    此接收者是用来接收Sdcard卸载的广播。当Sdcard从手机中分离出来的时候,Sdcard中的媒体文件相对应的数据库将无法操作。

       1: private void detachVolume(Uri uri) {
       2:        //判断是否是同一个进程
       3:        if (Process.supportsProcesses() && Binder.getCallingPid() != Process.myPid()) {
       4:            throw new SecurityException(
       5:                    "Opening and closing databases not allowed.");
       6:        }
       7:         //此方法只是操作Sdcard的媒体数据库,不支持手机内存的媒体数据库
       8:        String volume = uri.getPathSegments().get(0);
       9:        if (INTERNAL_VOLUME.equals(volume)) {
      10:            throw new UnsupportedOperationException(
      11:                    "Deleting the internal volume is not allowed");
      12:        } else if (!EXTERNAL_VOLUME.equals(volume)) {
      13:            throw new IllegalArgumentException(
      14:                    "There is no volume named " + volume);
      15:        }
      16:  
      17:        synchronized (mDatabases) {
      18:            DatabaseHelper database = mDatabases.get(volume);
      19:            if (database == null) return;
      20:  
      21:            try {
      22:                // touch the database file to show it is most recently used
      23:                File file = new File(database.getReadableDatabase().getPath());
      24:                file.setLastModified(System.currentTimeMillis());
      25:            } catch (SQLException e) {
      26:                Log.e(TAG, "Can't touch database file", e);
      27:            }
      28:             //移除数据库
      29:            mDatabases.remove(volume);
      30:            database.close();
      31:        }
      32:  
      33:        getContext().getContentResolver().notifyChange(uri, null);
      34:        if (LOCAL_LOGV) Log.v(TAG, "Detached volume: " + volume);
      35:    }

    注意移除数据库并非删除数据库文件(*.db),mDatabases是一个HashMap<String,DatabaseHelper>,移除的含义是暂时无法操作,也可以说说是查询返回的数据都是空的。

    2.MediaScannerCursor

    一个自定义游标,用来查询媒体文件的扫描状态。主要有一个volume字段,用来区分是内置媒体数据库还是Sdcard的媒体数据库。

    3.MediaScannerReceiver

    此类实现广播接收者。接收到广播的时候对手机的媒体文件进行扫描。

       1: public class MediaScannerReceiver extends BroadcastReceiver
       2: {
       3:     private final static String TAG = "MediaScannerReceiver";
       4:  
       5:     @Override
       6:     public void onReceive(Context context, Intent intent) {
       7:         String action = intent.getAction();
       8:         Uri uri = intent.getData();
       9:         String externalStoragePath = Environment.getExternalStorageDirectory().getPath();
      10:         //系统启动完毕
      11:         if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
      12:             // scan internal storage
      13:             scan(context, MediaProvider.INTERNAL_VOLUME);
      14:         } else {
      15:             if (uri.getScheme().equals("file")) {
      16:                 // handle intents related to external storage
      17:                 String path = uri.getPath();
      18:                 if (action.equals(Intent.ACTION_MEDIA_MOUNTED/*Sdcard挂载广播*/) && 
      19:                         externalStoragePath.equals(path)) {
      20:                     scan(context, MediaProvider.EXTERNAL_VOLUME);
      21:                 } else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE/*单个文件扫描广播*/) &&
      22:                         path != null && path.startsWith(externalStoragePath + "/")) {
      23:                     scanFile(context, path);
      24:                 }
      25:             }
      26:         }
      27:     }

    扫描分为两种三种情况:

    a,启动完毕扫面手机内存中的媒体文件

    b.sdcard挂载完毕扫描扩展卡的媒体文件

    c,扫描单个文件

    应用实例:我们可以发送不同的广播让系统去扫描媒体文件。当需要扫描单个文件的时候需要设置一些参数,如下:

       1: /**
       2:      * 扫描文件
       3:      * 
       4:      * @param filePath 文件路径
       5:      * @author http://t.sina.com.cn/halzhang
       6:      */
       7:     public void scanOneFile(final String filePath) {
       8:         Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
       9:         Uri uri = Uri.parse("file://" + filePath);
      10:         intent.setData(uri);
      11:         sendBroadcast(intent);
      12:     }

    接着看一下scanscenFile两个方法:

       1: private void scan(Context context, String volume/*内置卡或者外置卡*/) {
       2:        Bundle args = new Bundle();
       3:        args.putString("volume", volume);
       4:        context.startService(
       5:                new Intent(context, MediaScannerService.class).putExtras(args));
       6:    }    
       7:  
       8:    private void scanFile(Context context, String path/*文件路径*/) {
       9:        Bundle args = new Bundle();
      10:        args.putString("filepath", path);
      11:        context.startService(
      12:                new Intent(context, MediaScannerService.class).putExtras(args));
      13:    }  

    两个方法都是启动MediaScannerService去扫描媒体文件的。

    关于MediaScannerSerive且听下回分解。

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

  • 相关阅读:
    Akka(33): Http:Marshalling,to Json
    Akka(32): Http:High-Level-Api,Route exception handling
    Akka(31): Http:High-Level-Api,Route rejection handling
    Akka(30): Http:High-Level-Api,Routing DSL
    Akka(29): Http:Server-Side-Api,Low-Level-Api
    Akka(28): Http:About Akka-Http
    Akka(27): Stream:Use case-Connecting Slick-dbStream & Scalaz-stream-fs2
    Akka(26): Stream:异常处理-Exception handling
    Akka(25): Stream:对接外部系统-Integration
    Akka(24): Stream:从外部系统控制数据流-control live stream from external system
  • 原文地址:https://www.cnblogs.com/halzhang/p/1976178.html
Copyright © 2020-2023  润新知