Android开发——MediaProvider源码分析(1)
2011-03-07 20:40 HalZhang 阅读(16834) 评论(7) 编辑 收藏 举报--------------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: }
接着看一下scan和scenFile两个方法:
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--------------