Android MediaProvider數據庫模式


Android MediaProvider數據庫模式

原文地址

摘要: Android MediaProvider 使用 SQLite 數據庫存儲圖片、視頻、音頻等多媒體文件的信息,供視頻播放器、音樂播放器、圖庫使用。本文詳細分析了 Android MediaProvider 多媒體數據庫(以 SDK 2.3.3 為例)的模式(schema),並簡要敘述與系統媒體掃描服務 MediaScanner 的交互。

 

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 語句,也就是數據庫模式,具體如下(單擊展開代碼):

 

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 值作為主鍵,並建立了索引。索引可以加快數據查找速度,但由於需要維護索引所以插入/刪除等寫入操作速度會變慢。索引如下:

CREATE INDEX album_id_idx on audio_meta(album_id);
2 CREATE INDEX album_idx on albums(album);
3 CREATE INDEX albumkey_index on albums(album_key);
4 CREATE INDEX artist_id_idx on audio_meta(artist_id);
5 CREATE INDEX artist_idx on artists(artist);
6 CREATE INDEX artistkey_index on artists(artist_key);
7 CREATE INDEX image_bucket_index ON images(bucket_id, datetaken);
8 CREATE INDEX image_id_index on thumbnails(image_id);
9 CREATE INDEX sort_index on images(datetaken ASC, _id ASC);
10 CREATE INDEX title_idx on audio_meta(title);
11 CREATE INDEX titlekey_index on audio_meta(title_key);
12 CREATE INDEX video_bucket_index ON video(bucket_id, datetaken);
13

CREATE INDEX video_id_index on videothumbnails(video_id);

 

1 CREATE INDEX album_id_idx on audio_meta(album_id);
2 CREATE INDEX album_idx on albums(album);
3 CREATE INDEX albumkey_index on albums(album_key);
4 CREATE INDEX artist_id_idx on audio_meta(artist_id);
5 CREATE INDEX artist_idx on artists(artist);
6 CREATE INDEX artistkey_index on artists(artist_key);
7 CREATE INDEX image_bucket_index ON images(bucket_id, datetaken);
8 CREATE INDEX image_id_index on thumbnails(image_id);
9 CREATE INDEX sort_index on images(datetaken ASC, _id ASC);
10 CREATE INDEX title_idx on audio_meta(title);
11 CREATE INDEX titlekey_index on audio_meta(title_key);
12 CREATE INDEX video_bucket_index ON video(bucket_id, datetaken);
13 CREATE INDEX video_id_index on videothumbnails(video_id);

由於比較簡單就不解釋了,要深入了解索引可以參考這個關於 SQL Server 的分析MySQL索引背后的數據結構及算法原理,原理應該是差不多的。

視圖

視圖類似於表,但並非獨立存在,是從其他表里面查詢數據得到的。使用視圖可以加快數據庫查詢速度,不用每次都執行復雜的 SQL 語句查詢。圖如下所示:

Note: 如何看視圖:圖下面的部分是數據來源的表,中間是從表中選取的字段,但類似於 COUNT 等 SQL 查詢操作無法在圖上體現,最好還是看實際 SQL 語句。

Note: SQLite 當中視圖都是只讀的,也就是說不能對視圖進行插入、更新、刪除等操作。但是可以在視圖建立 INSTEAD OF 觸發器來達到同樣的目的,多媒體數據庫當中的 audio_delete 觸發器就是如此。

觸發器

觸發器是為了維護數據庫刪除操作而建立的,因為所刪除的表可能與另外的表有關系,需要同時刪除另外一個表的字段。可以看以下一個例子:

CREATE TRIGGER audio_meta_cleanup
2 DELETE ON audio_meta
3 BEGIN
4     DELETE FROM audio_genres_map WHERE audio_id = old._id;
5     DELETE FROM audio_playlists_map WHERE audio_id = old._id;
6 END;
1 CREATE TRIGGER audio_meta_cleanup
2 DELETE ON audio_meta
3 BEGIN
4     DELETE FROM audio_genres_map WHERE audio_id = old._id;
5     DELETE FROM audio_playlists_map WHERE audio_id = old._id;
6 END;

這是關於 audio_meta 表的觸發器,意思是當刪除此表上的記錄時,同時刪除 audio_genres_map 表上 audio_id 與此表 id 相同的記錄,刪除 audio_playlists_map 表上 audio_id 與此表 id 相同的記錄。這樣當刪除 audio_meta 表的記錄時,另外兩個表的相應記錄也會自動刪除,不會由於漏刪除而殘留多余數據。

3. 如何維護數據庫

插入

插入、更新主要由 MediaScanner 進行,當刪除/移動媒體文件時 MediaScanner 會掃描磁盤並更新數據庫。數據插入主要在 endFile() 方法中進行,例如插入音頻記錄時相關的表都會插入相應的記錄。而圖片、視頻縮略圖,專輯封面這幾個則是第一次取圖片的時候才會生成縮略圖保存到磁盤,並把記錄插入到數據庫中。

刪除

刪除操作主要由觸發器維護。例如當一個應用刪除圖片時,一般只會刪除圖片數據庫,所以必須要有觸發器同時刪除縮略圖數據庫。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM