当前位置: 首页 > java >正文

多媒体文件管理-数据库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-permissionandroid: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,volumeIdfinal 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,	//MusicEnvironment.DIRECTORY_PODCASTS,	//PodcastsEnvironment.DIRECTORY_RINGTONES,	//RingtonesEnvironment.DIRECTORY_ALARMS,	//AlarmsEnvironment.DIRECTORY_NOTIFICATIONS,	//NotificationsEnvironment.DIRECTORY_PICTURES,	//PicturesEnvironment.DIRECTORY_MOVIES,	//MoviesEnvironment.DIRECTORY_DOWNLOADS,	//DownLoadsEnvironment.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); }








 

 

 

http://www.xdnf.cn/news/10902.html

相关文章:

  • Android4.1新特性及新增API
  • 国内系统加速软件类第一品牌: 系统加速精灵 V3.2.3 绿色版
  • 网络嗅探,大神都在用这10个抓包工具
  • spoolsv.exe打印木马清除及补救方法
  • 如何关闭445端口 两种方式教你关闭445端口
  • TivaC学习笔记
  • 65个源代码网站
  • 电脑监控软件安装步骤(监控软件怎么安装在电脑上?)
  • 手机链接WiFi 网络速度慢的5个原因及解决方法
  • Android Market 账号注册和应用发布教程
  • Exception in thread “main“ java.lang.NumberFormatException
  • RegularExpressionValidator验证控件的验证表达式介绍
  • CNVD-2019-48814 Weblogic wls9_async_response 反序列化RCE漏洞复现
  • jspsmart实现文件上传下载及jspSmartUpload.jar下载
  • 译云API申请方法
  • Dreamweaver CS4:完美世界的强大工具
  • Win7下VS2008破解方法
  • C语言初阶——手把手教零基础/新手入门(万字心得笔记)
  • 终身伴侣(两个人的网站)代码+效果演示(文末源码地址)
  • ubuntu 12.10已经发布
  • Hibernate 中的attachDirty,attachClean,merge之间的区别
  • 常用OCR软件介绍
  • LOADRUNNER8.1操作笔记
  • 《Ghost Win7 SP1 电脑商装机版 V3.0》64位(电脑疯子作品)
  • 张国荣:去世前深受新欢旧爱拉锯战折磨(图
  • Chromeplus(枫树浏览器):基于谷歌Chrome的双核安全浏览器
  • 简化 java8流式处理工具
  • 百谷歌---学习工作好东西啊!!
  • 验证嵌入式ARM32环境中4G模块的有效方法
  • Timer定时器简单的两种用法