多媒体文件管理-数据库external.db,internal.db (一)

多媒体数据库路径:data/data/com.android.providers.media/database/external.db||internal.db

多媒体文件管理主要组成部分:

1),MediaScannerService,扫描多媒体文件。

      扫描的对象包括内部、外部存储设备,它继承自service,并实现了Runnable接口,在一个独立的线程中执行扫描操作。

      也会扫描文件,文件扫描完成时会有回调scanCompleted()@IMediaScannerListener

2),MediaProvider,存储媒体文件信息。

       MediaScannerService扫描到的结果,由MediaProvider完成存储。

3),MediaStore,查询入口。

       MediaProvider相当于存储文件的仓库,而MediaStore相当于展示媒体文件的柜台。当你想查看一个媒体文件时,通常是从柜台入手。

       MediaStore把所有的文件分为几类:

       MediaStore.java

       MediaStore.Files所有的文件,包括非多媒体文件。

       MediaStore. InternalThumbnails,这个类是被图像缩略图,视频缩略图内部使用的。它没有提供uri,所以别的地方应该访问不了。

       MediaStore. Images,图像文件存储的地方。

       MediaStore.Audio,音频文件类存储的地方。

       MediaStore.Video,视频文件类存储的地方。

      每种类型都可以通过getContentUri()接口获取具体的引用位置。

MediaStore.java,

URI中用到的常量定义:

public static final String AUTHORITY ="media";
private static final StringCONTENT_AUTHORITY_SLASH = "content://" +
AUTHORITY +"/";
Volume 分internal和 external。

public static final class Files {
//文件files对应的uri:content://media/external/file ,其中volume是external。
//也就是external这个数据库的files这张表的uri。
	public static Uri getContentUri(String volumeName) {
		return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName + "/file");
}
}
图像又分media和thumbnails
public static final class Images {
	public static final class Media  implements  ImageColumns {
	public static Uri getContentUri(String volumeName) {
		return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
			"/images/media");
	}
}
public static class Thumbnails  implements  BaseColumns {
	public static Uri getContentUri(String volumeName) {
		return Uri.parse(CONTENT_AUTHORITY_SLASH + volumeName +
		"/images/thumbnails");
	}
}
}

接着看下处理文件存储的MediaProvider,主要工作是创建用于存储各种媒体信息的数据库,并提供更新、查询等操作。

android\packages\providers\MediaProvider

先看他的Androidmanifest.xml中对权限的要求:

AndroidManifest.xml


<provider android:name="MediaProvider" android:authorities="media"
	android:multiprocess="false" android:exported="true">
//这个元素用于给内容提供器的数据子集授权,数据子集是由content:uri的路径部分来标识的。授权是提供器给客户端提供的一种能力,这样客户端就可以访问通常没有权限访问的数据,从而克服基于单次访问的权限。
如果android:grantUriPermissions="true",那么权限能够被授予内容提供器范围内的任何数据。如果android:grantUriPermissions="false,那么权限只能授予这个元素指定的数据子集。
android:pathPrefix 属性指定了路径的初始部分,权限能够被授予共享这个初始路径的所有数据子集。
android:path 属性指定了一个完整路径,权限只能被授予这个路径所标识的具体的数据子集。
android:pathPattern 属性定义了一个完整的uri路径,但这个uri中包含了通配符。
	<grant-uri-permission android:pathPrefix="/external/" />
//这个元素用于定义内容提供器中的具体的数据子集的路径,及必要的权限。
android:permission 这个属性定义了一个权限名称,为了读写内容提供器的数据,客户端必须要有这个权限。这个属性是给数据设置读写权限的便利方法,但是readPermission和writePermission属性比这个优先级高。
Android:readpermission 读取内容提供器中的数据,客户端必须要有这个权限。
Android:writePermission 修改内容提供器中的数据,客户端必须要有这个权限。
	<path-permission
		android:pathPrefix="/external/"
		android:readPermission="android.permission.READ_EXTERNAL_STORAGE"
		android:writePermission="android.permission.WRITE_EXTERNAL_STORAGE" />
</provider>

下面看mediaprovider是如何创建数据库的:

//附加数据库到存储盘符(internal or external)上。
private Uri attachVolume(String volume) @MediaProvider.java{
	DatabaseHelper helper = null;
//如果数据库已经存在了,就不需要重复创建了。mDatabases是一个hashmap,统管所有的DatabaseHelper。ensureDefaultFolders这个函数会创建一些默认的文件夹。
	helper = mDatabases.get(volume);
	if (helper != null) {
		if (EXTERNAL_VOLUME.equals(volume)) {
			ensureDefaultFolders(helper, helper.getWritableDatabase());
		}
	return Uri.parse("content://media/" + volume);
//这是内部存储设备,创建的数据库是:INTERNAL_DATABASE_NAME = "internal.db";
	if (INTERNAL_VOLUME.equals(volume)) {
		helper = new DatabaseHelper(context, INTERNAL_DATABASE_NAME, true,	
			false, mObjectRemovedCallback);
} else if (EXTERNAL_VOLUME.equals(volume)) {
//获取外部存储卷轴的id,volumeId
	final StorageVolume actualVolume = mStorageManager.getPrimaryVolume();
	final int volumeId = actualVolume.getFatVolumeId();
//如果外部存储设备还没挂载,会新创建一个名字为external-****.db的数据库,而不是我们期望的数据库名,然后,如果android.process.media进程(mediaprovider所属的进程)被杀掉了或重启了,真正的外部存储设备会被附加上。
	String dbName = "external-" + Integer.toHexString(volumeId) + ".db";
	helper = new DatabaseHelper(context, dbName, false,
		false, mObjectRemovedCallback);
	mVolumeId = volumeId;
}
//将刚创建的database添加到hashmap中。
mDatabases.put(volume, helper);
}

具体看下ensureDefaultFolders()函数,主要创建了默认的文件夹,都创建了那些文件夹的呢?

private static final String[] sDefaultFolderNames = {
        Environment.DIRECTORY_MUSIC,	//Music
        Environment.DIRECTORY_PODCASTS,	//Podcasts
        Environment.DIRECTORY_RINGTONES,	//Ringtones
        Environment.DIRECTORY_ALARMS,	//Alarms
        Environment.DIRECTORY_NOTIFICATIONS,	//Notifications
        Environment.DIRECTORY_PICTURES,	//Pictures
        Environment.DIRECTORY_MOVIES,	//Movies
        Environment.DIRECTORY_DOWNLOADS,	//DownLoads
        Environment.DIRECTORY_DCIM,	//Dcim
    };
private void ensureDefaultFolders(DatabaseHelper helper, SQLiteDatabase db)
@ MediaProvider.java {
//获取主存储设备。
	final StorageVolume vol = mStorageManager.getPrimaryVolume();
	final String key;
	if (VolumeInfo.ID_EMULATED_INTERNAL.equals(vol.getId())) {
		key = "created_default_folders";
	} else {
		key = "created_default_folders_" + vol.getUuid();
	}
	final SharedPreferences prefs = 
		PreferenceManager.getDefaultSharedPreferences(getContext());
	if (prefs.getInt(key, 0) == 0) {
		for (String folderName : sDefaultFolderNames) {
			final File folder = new File(vol.getPathFile(), folderName);
			if (!folder.exists()) {
	//创建文件夹,并插入到数据库。
				folder.mkdirs();
				insertDirectory(helper, db, folder.getAbsolutePath());
			}
		}
	}
//这些文件的创建只做一次,如果用户手动删除了,后面也不会在创建。除非恢复出厂值,或者这个模块的缓存清了,SharedPreferences就是控制这个的。
	SharedPreferences.Editor editor = prefs.edit();
	editor.putInt(key, 1);
	editor.commit();
}

分析过数据库的创建,结合query,看下数据库中都提供哪些table。

public Cursor query(Uri uri, String[] projectionIn, String selection, String[] selectionArgs, String 
sort)@ MediaProvider.java {
//step1,根据uri匹配table。URI_MATCHER是在mediaprovider创建之初通过addURI添加了匹配选项。
如:URI_MATCHER.addURI("media", "*/images/media", IMAGES_MEDIA);第一个参数是Authority,第二个参数是path,最后一个参数是uri匹配成功时的返回值。
	int table = URI_MATCHER.match(uri);
//通过uri来选择相应的数据库。如果之前数据库还没创建,这里就需要真正的去生成这个数据库实例。
	DatabaseHelper helper = getDatabaseForUri(uri);
//数据库查询业务构造器,用于构建各种查询参数。
	SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
//各个类型的表,查询过程类似,这里只列举了一个示例。
	switch (table) {
		case IMAGES_MEDIA_ID:
//设置要查询的表,通过URI_MATCHER得到的table值并不是数据库里的表名,这里setTables的才是表名。
			qb.setTables("images");
//sql查询语句的where。
			qb.appendWhere("_id=?");
			prependArgs.add(uri.getPathSegments().get(3));
			break;

case VIDEO_MEDIA:

qb.setTables("video");

caseIMAGES_MEDIA_ID:

qb.setTables("images");

case AUDIO_MEDIA:

qb.setTables("audio_meta");

case AUDIO_MEDIA_ID:

qb.setTables("audio");

caseAUDIO_MEDIA_ID_GENRES:

qb.setTables("audio_genres");

caseAUDIO_MEDIA_ID_PLAYLISTS:

qb.setTables("audio_playlists");

case AUDIO_GENRES:

qb.setTables("audio_genres");

caseAUDIO_PLAYLISTS:

qb.setTables("audio_playlists");

case AUDIO_ALBUMS:

qb.setTables("audio_meta");

case MEDIA_BOOKMARK:

qb.setTables("bookmarks");

 } //把各种查询条件合并成查询语句,通过qb发出查询请求。 Cursor c = qb.query(db, projectionIn, selection, ombine(prependArgs, selectionArgs), groupBy, null, sort, limit); }








 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值