文章来源:http://www.otechu.me/zh/2011/10/schema-of-android-mediaprovider-database/

Android MediaProvider数据库模式

摘要: Android MediaProvider 使用 SQLite 数据库存储图片、视频、音频等多媒体文件的信息,供视频播放器、音乐播放器、图库使用。本文详细分析了 Android MediaProvider 多媒体数据库(以 SDK 2.3.3 为例)的模式(schema),并简要叙述与系统媒体扫描服务 MediaScanner 的交互。

2012-6-14更新:要了解 Android 4.0(ICS)数据库模式的更新请看Android MediaProvider数据库模式(二):ICS的更改

1. 如何提取数据库

以 root 权限进入 adb shell,使用 sqlite3 打开位于手机上/data/data/com.android.providers.media/databases上的一个数据库。以 external 开头的数据库存储的是 SD 卡媒体信息,一张卡对应一个,所以如果手机使用过多张卡会有多个数据库。以 internal 开头的数据库存储手机内部存储器的媒体信息。因为一般用户无法访问手机内部存储器,而且这两个数据库结构是大体上是相同的,所以只需要关注 external 数据库即可。

Note:数据库都是以类似 external-ffffffff.db 的形式命名的, 后面的 8 个 16 进制字符是该 SD 卡 FAT 分区的 Volume ID。该 ID 是分区时决定的,只有重新分区或者手动改变才会更改,可以防止插入不同 SD 卡时数据库冲突。要简单了解 FAT 文件系统请看Understanding FAT Filesystems

接着在 sqlite3 执行命令 .schema 即可导出创建数据库的 SQL 语句,也就是数据库模式,具体如下(单击展开代码):

1 CREATETABLEalbum_art (album_idINTEGERPRIMARYKEY,_data TEXT);
2 CREATETABLEalbums (album_idINTEGERPRIMARYKEY,album_key TEXTNOTNULLUNIQUE,album TEXTNOTNULL);
3 CREATETABLEandroid_metadata (locale TEXT);
4 CREATETABLEartists (artist_idINTEGERPRIMARYKEY,artist_key TEXTNOTNULLUNIQUE,artist TEXTNOTNULL);
5 CREATETABLEaudio_genres (_idINTEGERPRIMARYKEY,nameTEXTNOTNULL);
6 CREATETABLEaudio_genres_map (_idINTEGERPRIMARYKEY,audio_idINTEGERNOTNULL,genre_idINTEGERNOTNULL);
7 CREATETABLEaudio_meta (_idINTEGERPRIMARYKEY,_data TEXTUNIQUENOTNULL,_display_name TEXT,_sizeINTEGER,mime_type TEXT,date_addedINTEGER,date_modifiedINTEGER,title TEXTNOTNULL,title_key TEXTNOTNULL,durationINTEGER,artist_idINTEGER,composer TEXT,album_idINTEGER,trackINTEGER,yearINTEGERCHECK(year!=0),is_ringtoneINTEGER,is_musicINTEGER,is_alarmINTEGER,is_notificationINTEGER, is_podcastINTEGER, bookmarkINTEGER);
8 CREATETABLEaudio_playlists (_idINTEGERPRIMARYKEY,_data TEXT,nameTEXTNOTNULL,date_addedINTEGER,date_modifiedINTEGER);
9 CREATETABLEaudio_playlists_map (_idINTEGERPRIMARYKEY,audio_idINTEGERNOTNULL,playlist_idINTEGERNOTNULL,play_orderINTEGERNOTNULL);
10 CREATETABLEimages (_idINTEGERPRIMARYKEY,_data TEXT,_sizeINTEGER,_display_name TEXT,mime_type TEXT,title TEXT,date_addedINTEGER,date_modifiedINTEGER,description TEXT,picasa_id TEXT,isprivateINTEGER,latitudeDOUBLE,longitudeDOUBLE,datetakenINTEGER,orientationINTEGER,mini_thumb_magicINTEGER,bucket_id TEXT,bucket_display_name TEXT);
11 CREATETABLEthumbnails (_idINTEGERPRIMARYKEY,_data TEXT,image_idINTEGER,kindINTEGER,widthINTEGER,heightINTEGER);
12 CREATETABLEvideo (_idINTEGERPRIMARYKEY,_data TEXTNOTNULL,_display_name TEXT,_sizeINTEGER,mime_type TEXT,date_addedINTEGER,date_modifiedINTEGER,title TEXT,durationINTEGER,artist TEXT,album TEXT,resolution TEXT,description TEXT,isprivateINTEGER,tags TEXT,category TEXT,language TEXT,mini_thumb_data TEXT,latitudeDOUBLE,longitudeDOUBLE,datetakenINTEGER,mini_thumb_magicINTEGER, bucket_id TEXT, bucket_display_name TEXT, bookmarkINTEGER);
13 CREATETABLEvideothumbnails (_idINTEGERPRIMARYKEY,_data TEXT,video_idINTEGER,kindINTEGER,widthINTEGER,heightINTEGER);
14
15 CREATEVIEWalbum_infoASSELECTaudio.album_idAS_id, album, album_key,MIN(year)ASminyear,MAX(year)ASmaxyear, artist, artist_id, artist_key,count(*)ASnumsongs,album_art._dataASalbum_artFROMaudioLEFTOUTERJOINalbum_artONaudio.album_id=album_art.album_idWHEREis_music=1GROUPBYaudio.album_id;
16 CREATEVIEWartist_infoASSELECTartist_idAS_id, artist, artist_key,COUNT(DISTINCTalbum_key)ASnumber_of_albums,COUNT(*)ASnumber_of_tracksFROMaudioWHEREis_music=1GROUPBYartist_key;
17 CREATEVIEWartists_albums_mapASSELECTDISTINCTartist_id, album_idFROMaudio_meta;
18 CREATEVIEWaudioasSELECT*FROMaudio_metaLEFTOUTERJOINartistsONaudio_meta.artist_id=artists.artist_idLEFTOUTERJOINalbumsONaudio_meta.album_id=albums.album_id;
19 CREATEVIEWsearchASSELECT_id,'artist'ASmime_type,artist,NULLASalbum,NULLAStitle,artistAStext1,NULLAStext2,number_of_albumsASdata1,number_of_tracksASdata2,artist_keyASmatch,'content://media/external/audio/artists/'||_idASsuggest_intent_data,1ASgrouporderFROMartist_infoWHERE(artist!='<unknown>')UNIONALLSELECT_id,'album'ASmime_type,artist,album,NULLAStitle,albumAStext1,artistAStext2,NULLASdata1,NULLASdata2,artist_key||' '||album_keyASmatch,'content://media/external/audio/albums/'||_idASsuggest_intent_data,2ASgrouporderFROMalbum_infoWHERE(album!='<unknown>')UNIONALLSELECTsearchhelpertitle._idAS_id,mime_type,artist,album,title,titleAStext1,artistAStext2,NULLASdata1,NULLASdata2,artist_key||' '||album_key||' '||title_keyASmatch,'content://media/external/audio/media/'||searchhelpertitle._idASsuggest_intent_data,3ASgrouporderFROMsearchhelpertitleWHERE(title !='');
20 CREATEVIEWsearchhelpertitleASSELECT*FROMaudioORDERBYtitle_key;
21
22 CREATEINDEXalbum_id_idxonaudio_meta(album_id);
23 CREATEINDEXalbum_idxonalbums(album);
24 CREATEINDEXalbumkey_indexonalbums(album_key);
25 CREATEINDEXartist_id_idxonaudio_meta(artist_id);
26 CREATEINDEXartist_idxonartists(artist);
27 CREATEINDEXartistkey_indexonartists(artist_key);
28 CREATEINDEXimage_bucket_indexONimages(bucket_id, datetaken);
29 CREATEINDEXimage_id_indexonthumbnails(image_id);
30 CREATEINDEXsort_indexonimages(datetakenASC, _idASC);
31 CREATEINDEXtitle_idxonaudio_meta(title);
32 CREATEINDEXtitlekey_indexonaudio_meta(title_key);
33 CREATEINDEXvideo_bucket_indexONvideo(bucket_id, datetaken);
34 CREATEINDEXvideo_id_indexonvideothumbnails(video_id);
35
36 CREATETRIGGERalbumart_cleanup1DELETEONalbumsBEGINDELETEFROMalbum_artWHEREalbum_id = old.album_id;END;
37 CREATETRIGGERalbumart_cleanup2DELETEONalbum_artBEGINSELECT_DELETE_FILE(old._data);END;
38 CREATETRIGGERaudio_deleteINSTEADOFDELETEONaudioBEGINDELETEfromaudio_metawhere_id=old._id;DELETEfromaudio_playlists_mapwhereaudio_id=old._id;DELETEfromaudio_genres_mapwhereaudio_id=old._id;END;
39 CREATETRIGGERaudio_genres_cleanupDELETEONaudio_genresBEGINDELETEFROMaudio_genres_mapWHEREgenre_id = old._id;END;
40 CREATETRIGGERaudio_meta_cleanupDELETEONaudio_metaBEGINDELETEFROMaudio_genres_mapWHEREaudio_id = old._id;DELETEFROMaudio_playlists_mapWHEREaudio_id = old._id;END;
41 CREATETRIGGERaudio_playlists_cleanupDELETEONaudio_playlistsBEGINDELETEFROMaudio_playlists_mapWHEREplaylist_id = old._id;SELECT_DELETE_FILE(old._data);END;
42 CREATETRIGGERimages_cleanupDELETEONimagesBEGINDELETEFROMthumbnailsWHEREimage_id = old._id;SELECT_DELETE_FILE(old._data);END;
43 CREATETRIGGERthumbnails_cleanupDELETEONthumbnailsBEGINSELECT_DELETE_FILE(old._data);END;
44 CREATETRIGGERvideo_cleanupDELETEONvideoBEGINSELECT_DELETE_FILE(old._data);END;
45 CREATETRIGGERvideothumbnails_cleanupDELETEONvideothumbnailsBEGINSELECT_DELETE_FILE(old._data);END;

Note: 如果手机没有 sqlite3 程序,可以搜索编译过的源代码的 out 目录找到可执行文件,大约 90kb,然后 adb push 到手机的 /system/bin/ 目录。安装 sqlite3、查询数据库均需要 adb root 权限。 Android 的多媒体数据库主要由表、视图、索引以及触发器组成。

接着还需要把数据库转换成图,手工转换的话就是根据 SQL 语句自行画图;推荐懒人使用自动转换,先使用 adb pull 把数据库导出,再使用 Power Designer 或者 Visio 的逆向工程(Reverse Engineer)功能生成物理数据模型(Physical Data Model)。注意要连接 sqlite 数据库文件的话需要先安装 sqlite 的 ODBC 驱动,教程在这里:SQLite ODBC Driver

2. 数据库模式分析

图片数据库

图片数据库由两个表组成,分别是 images 和 thumbnails,物理数据模型如下所示(Power Designer 逆向工程生成)


Note: 如何数据库物理模型图:<pk> 表示此为主键。其余的表名、字段名、数据类型应该都能看明白。

Note: SQLite 从 3.6.19 版才开始支持外键约束,Android 2.3.3 使用的是 3.7.x,但并没有使用此特性,而是通过操作数据库的程序(如 MediaScanner)以及触发器来维护数据库的一致性。这里可以了解 SQLite 的外键支持情况

数据表字段解析如下:

images:图片信息
字段 解析
_id 主键。图片 id,从 1 开始自增
_data 图片绝对路径
_size 文件大小,单位为 byte
_display_name 文件名
mime_type 类似于 image/jpeg 的 MIME 类型
title 不带扩展名的文件名
date_added 添加到数据库的时间,单位秒
date_modified 文件最后修改时间,单位秒
description
picasa_id 用于 picasa 网络相册
isprivate
latitude 纬度,需要照片有 GPS 信息
longitude 经度,需要照片有 GPS 信息
datetaken 取自 EXIF 照片拍摄时间,若为空则等于文件修改时间,单位毫秒
orientation 取自 EXIF 旋转角度,在图库旋转图片也会改变此值
mini_thumb_magic 取小缩略图时生成的一个随机数,见 MediaThumbRequest
bucket_id 等于 path.toLowerCase.hashCode(),见 MediaProvider.computeBucketValues()
bucket_display_name 直接包含图片的文件夹就是该图片的 bucket,就是文件夹名
thumbnails:缩略图
字段 解析
_id 主键。缩略图 id,从 1 开始自增
_data 图片绝对路径
image_id 缩略图所对应图片的 id,依赖于 images 表 _id 字段,可建立外键
kind 缩略图类型,1 是大缩略图,2 基本不用,3 是微型缩略图但其信息不保存在数据库
width 缩略图宽度
height 缩略图高度

视频数据库


数据表字段解析如下:

video:视频信息
字段 解析
_id 主键。视频 id
_data 视频绝对路径
_display_name 文件名
_size 文件大小,单位为 byte
mime_type 类似于 video/avi 的 MIME 类型
date_added 添加到数据库的时间,单位秒
date_modified 文件最后修改时间,单位秒
title 不带扩展名的文件名
duration 视频时长,单位毫秒
artist 艺术家
album 专辑名,一般为文件夹名
resolution
description
isprivate
tags
category
language
mini_thumb_data
latitude
longitude
datetaken
mini_thumb_magic 取小缩略图时生成的一个随机数,见 MediaThumbRequest
bucket_id 等于 path.toLowerCase.hashCode(),见 MediaProvider.computeBucketValues()
bucket_display_name 直接包含视频的文件夹就是该图片的 bucket,就是文件夹名
bookmark
videothumbnails:视频缩略图
字段 解析
_id 主键。缩略图 id
_data 缩略图绝对路径
video_id 缩略图所对应视频的 id,依赖于 video 表 _id 字段
kind 缩略图类型,1 是大图,视频只能取类型 1
width 缩略图宽度
height 缩略图高度

音频数据库

音频数据库是最复杂的,由 10 个表组成。物理数据模型如下所示:


album_art:专辑封面
字段 解析
album_id 主键。专辑 id
_data 专辑封面缓存的路径
albums:专辑信息
字段 解析
album_id 主键。专辑 id
album_key 全大写字母,用于字母索引
album 专辑名
android_metadata:当前字符编码
字段 解析
locale 默认字符编码,例如 zh_CN
artists:艺术家
字段 解析
artist_id 主键。艺术家 id
artist_key 全大写字母,用于字母索引
artist 艺术家
audio_genres:流派
字段 解析
_id 主键。流派 id
name 流派名称
audio_genres_map:音频流派映射
字段 解析
_id 主键。映射 id
audio_id 音频 id
genre_id 流派 id

Note: 为何要建立映射表:为了消除数据冗余。假如有大量音频属于同一流派,如果没有映射表则需要每个音频都需要记录同样的流派数据,有了映射表之后则只有一条记录就够了。这符合数据库设计的第三范式(the 3rd normal form)

audio_meta:音频信息
字段 解析
_id 主键。音频 id
_data 文件绝对路径
_display_name 文件名
_size 文件大小,单位 byte
mime_type 类似于 audio/mpeg 的 MIME 类型
date_added 添加到数据库的时间,单位秒
date_modified 文件最后修改时间,单位秒
title 来自 ID3 信息的标题,无则为不带扩展名的文件名
title_key 全大写字母的标题
duration 时长
artist_id 艺术家 id
composer 来自 ID3 信息,作曲家
album_id 专辑 id
track 来自 ID3 信息,音轨
year 来自 ID3 信息,年代
is_ringtone 是否铃声,0 或 1
is_music 是否音乐,1 才会在音乐播放器显示
is_alarm 是否闹钟铃声
is_notification 是否通知铃声
is_podcast 是否 podcast
bookmark
audio_playlists:播放列表
字段 解析
_id 主键。播放列表 id
_data
name 播放列表名
date_added
date_modified
audio_playlists_map:音频播放列表映射
字段 解析
_id 主键。映射 id
audio_id 音频 id
playlist_id 播放列表 id
play_order 播放顺序

索引

在 Android 数据库当中基本上使用自增 id 值作为主键,并建立了索引。索引可以加快数据查找速度,但由于需要维护索引所以插入/删除等写入操作速度会变慢。索引如下:

1 CREATEINDEXalbum_id_idxonaudio_meta(album_id);
2 CREATEINDEXalbum_idxonalbums(album);

更多相关文章

  1. Android(安卓)TextView 字段设置监听事件
  2. Android(安卓)SQLiteOpenHelper使用和源码详解
  3. 一篇搞定Android(安卓)Sqlite
  4. Android(安卓)Mediascanner实现机制
  5. Android(安卓)后台静默安装
  6. Android——四大组件、六大布局、五大存储
  7. android sqlite相关
  8. 查询备份Android(安卓)PDU彩信
  9. Android日历操作

随机推荐

  1. android studio 使用网络版本的gradle
  2. Android 控件之ImageSwitcher图片切换器
  3. android坐标图解
  4. Failed to fetch URL, SDK更新失败
  5. Android_如何静默安装
  6. /bin/repo: line 1: syntax error near u
  7. 【android】转载:实用Android开发工具和资
  8. Android(安卓)蓝牙4.0多蓝牙连接
  9. 如何向android的framework里添加新API
  10. Android编程心得-图片自适应心得