前言

Android多媒体相关的数据都存放在/data/data/com.android.providers.media/文件夹里的external.db和internal.db数据库里,二者的区别主要是前者是针对放在SDCard外部存储多媒体,后者主要针对放在手机本地存储里的多媒体资源。Android 6.0里两个数据库里的数据库表和表结构基本一致,这里只分析external中的表结构。

多媒体资源主要分为三大类图片、视频和音频,其中图片和视频都有缩略图资源,它们都是以文件的形式存在于Android系统中的。这些多媒体文件相关的信息会被记录在一个FILES数据库表中,为了能够更方便的查看不同类型的资源,数据库分别为图片视频和音频增加了视图对象。下面来逐个查看每个表和视图的生成DDL,了解每个字段的类型和用途。

文件数据表

数据表的创建DDL如下,可见每个字段都以下划线“_”开头:

CREATE TABLE "files" (
    _id INTEGER PRIMARY KEY AUTOINCREMENT, // 文件的id,从1开始逐渐增加
    _data TEXT UNIQUE COLLATE NOCASE, // 数据,实际是文件的保存路径
    _size INTEGER, // 多媒体文件的大小
    format INTEGER, // 多媒体文件类型
    parent INTEGER, // 父文件夹的id
    date_added INTEGER, // 多媒体文件添加时间
    date_modified INTEGER, // 多媒体文件修改时间
    mime_type TEXT, // 文件的媒体类型
    title TEXT, // 不带扩展名的文件名
    description TEXT, // 文件描述
    _display_name TEXT, // 带扩展名的文件名
    picasa_id TEXT, // ?
    orientation INTEGER, // 取自EXIF旋转角度,在图库旋转图片也会改变此值
    latitude DOUBLE, // 拍摄位置经纬度
    longitude DOUBLE,
    datetaken INTEGER, // 取自EXIF照片拍摄时间,若为空则等于文件修改时间,单位毫秒
    mini_thumb_magic INTEGER, // 取小缩略图时生成的一个随机数,见 MediaThumbRequest
    bucket_id TEXT, // 包含多媒体文件的bucket id 等于 path.toLowerCase.hashCode(),见 MediaProvider.computeBucketValues()
    bucket_display_name TEXT, // 直接包含图片的文件夹就是该图片的 bucket,就是文件夹名
    isprivate INTEGER,
    title_key TEXT,
    artist_id INTEGER, // 艺术家id,有一个艺术家表
    album_id INTEGER, // 专辑id,有一个专辑表
    composer TEXT, // 作曲者
    track INTEGER, // 音轨
    year INTEGER CHECK(year!=0), // 年代
    is_ringtone INTEGER, // 是否是铃声
    is_music INTEGER, // 是否是音乐
    is_alarm INTEGER, // 是不是闹钟铃声
    is_notification INTEGER, // 是不是通知铃声
    is_podcast INTEGER,
    album_artist TEXT, // 专辑艺术家
    duration INTEGER, // 持续时间
    bookmark INTEGER, 
    artist TEXT, // 艺术家展示名
    album TEXT, // 专辑展示名
    resolution TEXT, // 解析度
    tags TEXT, // 标签
    category TEXT, // 分类
    language TEXT, // 语言
    mini_thumb_data TEXT, 
    name TEXT,
    media_type INTEGER,
    old_id INTEGER,
    storage_id INTEGER,
    is_drm INTEGER,
    width INTEGER,  // 宽高信息
    height INTEGER);

从FILES表的字段可以看出,它包含了所有的图片、视频和音频文件信息,文件表记录了具体的文件路径和基本信息,其他Images等视图只需要连表查询就可以得到对应数据。

下面是在文件表上创建的索引对象,在查询使用到索引定义的字段时可以加快查询速度。

CREATE INDEX album_id_idx ON files(album_id);

CREATE INDEX artist_id_idx ON files(artist_id);

CREATE INDEX bucket_index on files(bucket_id,media_type,datetaken, _id);

CREATE INDEX bucket_name on files(bucket_id,media_type,bucket_display_name);

CREATE INDEX format_index ON files(format);

CREATE INDEX media_type_index ON files(media_type);

CREATE INDEX parent_index ON files(parent);

CREATE INDEX path_index ON files(_data);

CREATE INDEX sort_index ON files(datetaken ASC, _id ASC);

CREATE INDEX title_idx ON files(title);

CREATE INDEX titlekey_index ON files(title_key);

最后是触发器对象:

CREATE TRIGGER audio_playlists_cleanup DELETE ON files WHEN old.media_type=4 BEGIN DELETE FROM audio_playlists_map WHERE playlist_id = old._id;SELECT _DELETE_FILE(old._data);END;

CREATE TRIGGER files_cleanup DELETE ON files BEGIN SELECT _OBJECT_REMOVED(old._id);END;

图片资源

图片Images是一个视图对象,创建的DDL如下:

CREATE VIEW images AS 
    SELECT 
    _id, // 图片id
    _data, // 图片路径
    _size, // 图片大小
    _display_name, // 待扩展名的文件名
    mime_type, // 多媒体类型
    title, // 不带扩展名的文件类型
    date_added, // 图片添加时间
    date_modified, // 图片修改时间
    description, // 图片描述
    picasa_id, 
    isprivate,
    latitude, // EXIF里的经纬度
    longitude,
    datetaken, // EXIF里的日期
    orientation, // EXIF里的方向
    mini_thumb_magic, // 
    bucket_id, // 图片集id
    bucket_display_name, // 图片集名称
    width, // 图片宽高信息
    height  
    FROM files // 从文件表中查找media_type为1的记录就是图片多媒体资源 
    WHERE media_type=1;

可见Image视图所有的字段都取自files表,和图片相关的表还有一个ImageThumbnail图片缩略图表,它的DDL如下:

CREATE TABLE thumbnails (
    _id INTEGER PRIMARY KEY, // 缩略图id,从0开始组件增大
    _data TEXT, // 缩略图路径
    image_id INTEGER, // 缩略图对应的图片id
    kind INTEGER, // 缩略图类型缩略图类型,1 是大缩略图,2 基本不用,3 是微型缩略图但其信息不保存在数据库
    width INTEGER, // 缩略图宽高
    height INTEGER);

CREATE INDEX image_id_index on thumbnails(image_id);

因为大部分情况下是根据图片id查询对应的缩略图资源,在image_id上创建了索引对象加速查找。

视频资源

CREATE VIEW video AS 
    SELECT 
    _id, // 视频的id
    _data, // 视频的路径
    _display_name, // 不带扩展名的文件名
    _size, // 视频大小
    mime_type,
    date_added,
    date_modified,
    title,
    duration,
    artist,
    album,
    resolution, // 解析度
    description,
    isprivate,
    tags,
    category,
    language,
    mini_thumb_data,
    latitude,
    longitude,
    datetaken,
    mini_thumb_magic,
    bucket_id,
    bucket_display_name,
    bookmark,
    width,
    height 
FROM files 
WHERE media_type=3;

视频所有的字段都从files表里取,只不过视频的media_type是3。除了视频的视图之外还有一个视频缩略图表:

CREATE TABLE videothumbnails (
    _id INTEGER PRIMARY KEY, // 缩略图id
    _data TEXT, // 缩略图路径
    video_id INTEGER, // 缩略图对应的视频
    kind INTEGER, // 缩略图类型
    width INTEGER, // 缩略图宽高
    height INTEGER);

CREATE INDEX video_id_index on videothumbnails(video_id);

视频缩略图表和图片缩略图表基本上一致不再赘述。

音频资源

音频audio_meta视图的DDL对应如下:

CREATE VIEW audio_meta AS 
    SELECT 
        _id, // 音频id
        _data, // 音频文件路径
        _display_name, // 音频文件名不带扩展名
        _size, // 音频文件大小
        mime_type,
        date_added,
        is_drm,
        date_modified,
        title,
        title_key,
        duration,
        artist_id,
        composer,
        album_id,
        track,
        year,
        is_ringtone,
        is_music,
        is_alarm,
        is_notification,
        is_podcast,
        bookmark,
        album_artist 
    FROM files 
    WHERE media_type=2;

所有media_type为2的文件表记录就是音频文件。

再看艺术家和专辑两个表:

CREATE TABLE artists (
    artist_id INTEGER PRIMARY KEY, // 艺术家id
    artist_key TEXT NOT NULL UNIQUE, // 全大写字母,用于字母索引
    artist TEXT NOT NULL // 艺术家名称
);

CREATE INDEX artistkey_index on artists(artist_key);
CREATE INDEX artist_idx on artists(artist);


CREATE TABLE albums (
    album_id INTEGER PRIMARY KEY, // 专辑id
    album_key TEXT NOT NULL UNIQUE, // 全大写字母,用于字母索引
    album TEXT NOT NULL // 专辑名称
);

CREATE TRIGGER albumart_cleanup1 DELETE ON albums BEGIN DELETE FROM album_art WHERE album_id = old.album_id;END;

CREATE INDEX albumkey_index on albums(album_key);
CREATE INDEX album_idx on albums(album);

音频视图需要file表、artist表和album表联合查询,这样艺术家和专辑信息都会被包含在内。

CREATE VIEW audio as 
    SELECT * 
    FROM audio_meta
    LEFT OUTER JOIN artists 
    ON audio_meta.artist_id=artists.artist_id 
    LEFT OUTER JOIN albums 
    ON audio_meta.album_id=albums.album_id;