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

来源:互联网 发布:nginx 配置80端口转发 编辑:程序博客网 时间:2024/06/05 19:55

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中有个广播接收者,代码如下:

  

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


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

private void detachVolume(Uri uri) {         //判断是否是同一个进程         if (Process.supportsProcesses() && Binder.getCallingPid() != Process.myPid()) {             throw new SecurityException(                     "Opening and closing databases not allowed.");         }          //此方法只是操作Sdcard的媒体数据库,不支持手机内存的媒体数据库         String volume = uri.getPathSegments().get(0);         if (INTERNAL_VOLUME.equals(volume)) {             throw new UnsupportedOperationException(                     "Deleting the internal volume is not allowed");         } else if (!EXTERNAL_VOLUME.equals(volume)) {             throw new IllegalArgumentException(                     "There is no volume named " + volume);         }           synchronized (mDatabases) {             DatabaseHelper database = mDatabases.get(volume);             if (database == null) return;               try {                 // touch the database file to show it is most recently used                 File file = new File(database.getReadableDatabase().getPath());                 file.setLastModified(System.currentTimeMillis());             } catch (SQLException e) {                 Log.e(TAG, "Can't touch database file", e);             }              //移除数据库             mDatabases.remove(volume);             database.close();         }           getContext().getContentResolver().notifyChange(uri, null);         if (LOCAL_LOGV) Log.v(TAG, "Detached volume: " + volume);     }


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

2.MediaScannerCursor

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

3.MediaScannerReceiver

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

 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/*Sdcard挂载广播*/) &&                           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);                  }              }          }      }


扫描分为两种三种情况:

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

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

c,扫描单个文件

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

/**       * 扫描文件       *        * @param filePath 文件路径       * @author http://t.sina.com.cn/halzhang       */      public void scanOneFile(final String filePath) {          Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);          Uri uri = Uri.parse("file://" + filePath);          intent.setData(uri);          sendBroadcast(intent);      }


/**                     * update by Peng on 2011-10-18                     */                    //send a broadcast to mediascanner so that the music player can get the latest playlist                    if(mContext!=null){                    Intent intent=new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);                    Uri uri=Uri.parse("file:///mnt"+destFile.getAbsolutePath());                    intent.setData(uri);                    mContext.sendBroadcast(intent);                    }//或者 context.sendBroadcast(new Intent(                            Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://" + path)));//其中的path为绝对路径,如:/mnt/sdcard/filename。


接着看一下scan和scenFile两个方法:

  

 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));     }


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

关于MediaScannerSerive且听下回分解。
原创粉丝点击