メディアデータはデータベースによって管理されています。
Media Store APIを使ってメディアデータへアクセスしているのであれば、データベースの所在や構成などを意識する必要はありません。
ただ、「より複雑な制御をしたい場合」や「デバックを効率化したい場合」などに、データベースの知識が役立ちます。
今回は「メディアデータのデータベースによる管理」について、まとめます。
この記事は、Imagesデータ編です。
※環境:Android Studio Narwhal | 2025.1.1 Patch 1
データベースの所在
メディアデータのメディア情報を、データベースで管理しているのはMedia Providerモジュールです。
ですので、データベースは内部ストレージのMedia Providerモジュール固有の領域にあります。
# pwd /data/data/com.android.providers.media ← Media Storeアプリ固有の領域 # ls -l databases/* -rw-rw---- 1 u0_a60 u0_a60 155648 2025-06-11 13:12 databases/external.db -rw-rw---- 1 u0_a60 u0_a60 253952 2025-06-11 13:12 databases/internal.db ※「/data/data」は管理者ユーザー(ユーザーID:0)の内部ストレージと同じ場所
# pwd /data/data/com.google.android.providers.media.module ← Media Providerモジュール固有の領域 # ls -l databases/* -rw-rw---- 1 u0_a166 u0_a166 118784 2025-06-12 01:37 databases/external.db -rw------- 1 u0_a166 u0_a166 32768 2025-06-12 01:57 databases/external.db-shm -rw------- 1 u0_a166 u0_a166 453232 2025-06-12 01:56 databases/external.db-wal -rw-rw---- 1 u0_a166 u0_a166 233472 2025-06-12 01:38 databases/internal.db -rw------- 1 u0_a166 u0_a166 32768 2025-06-12 01:38 databases/internal.db-shm -rw------- 1 u0_a166 u0_a166 494432 2025-06-12 01:38 databases/internal.db-wal ※「/data/data」は管理者ユーザー(ユーザーID:0)の内部ストレージと同じ場所
テーブルの構成
データベースは複数のテーブルで構成されています。
# pwd /data/data/com.android.providers.media/databases # sqlite3 ./external.db sqlite> .tables album_art audio_genres images album_info audio_genres_map log albums audio_genres_map_noid search android_metadata audio_meta searchhelpertitle artist_info audio_playlists thumbnails artists audio_playlists_map video artists_albums_map downloads videothumbnails audio files
# pwd /data/data/com.google.android.providers.media.module/databases # sqlite3 ./external.db sqlite> .tables album_art audio_playlists log android_metadata audio_playlists_map search audio downloads searchhelpertitle audio_albums files thumbnails audio_artists images video audio_genres local_metadata videothumbnails
メディア情報の実態は、filesテーブルが持ちます。
imagesテーブルはfilesテーブルからimageメディア情報を切り出したview(仮想テーブル)です。
viewテーブルの条件に、media_typeカラム(media_type=1)が用いられています。
... db.execSQL("CREATE TABLE files (_id INTEGER PRIMARY KEY AUTOINCREMENT," + "_data TEXT UNIQUE COLLATE NOCASE,_size INTEGER,format INTEGER,parent INTEGER," + "date_added INTEGER,date_modified INTEGER,mime_type TEXT,title TEXT," + "description TEXT,_display_name TEXT,picasa_id TEXT,orientation INTEGER," + "latitude DOUBLE,longitude DOUBLE,datetaken INTEGER,mini_thumb_magic INTEGER," + "bucket_id TEXT,bucket_display_name TEXT,isprivate INTEGER,title_key TEXT," + "artist_id INTEGER,album_id INTEGER,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,is_drm INTEGER," + "width INTEGER, height INTEGER, title_resource_uri TEXT," + "owner_package_name TEXT DEFAULT NULL," + "color_standard INTEGER, color_transfer INTEGER, color_range INTEGER," + "_hash BLOB DEFAULT NULL, is_pending INTEGER DEFAULT 0," + "is_download INTEGER DEFAULT 0, download_uri TEXT DEFAULT NULL," + "referer_uri TEXT DEFAULT NULL, is_audiobook INTEGER DEFAULT 0," + "date_expires INTEGER DEFAULT NULL,is_trashed INTEGER DEFAULT 0," + "group_id INTEGER DEFAULT NULL,primary_directory TEXT DEFAULT NULL," + "secondary_directory TEXT DEFAULT NULL,document_id TEXT DEFAULT NULL," + "instance_id TEXT DEFAULT NULL,original_document_id TEXT DEFAULT NULL," + "relative_path TEXT DEFAULT NULL,volume_name TEXT DEFAULT NULL)"); ... db.execSQL("CREATE VIEW images AS SELECT " + String.join(",", getProjectionMap(Images.Media.class).keySet()) + " FROM files WHERE media_type=1"); ...
... db.execSQL("CREATE TABLE files (_id INTEGER PRIMARY KEY AUTOINCREMENT," + "_data TEXT UNIQUE COLLATE NOCASE,_size INTEGER,format INTEGER,parent INTEGER," + "date_added INTEGER,date_modified INTEGER,mime_type TEXT,title TEXT," + "description TEXT,_display_name TEXT,picasa_id TEXT,orientation INTEGER," + "latitude DOUBLE,longitude DOUBLE,datetaken INTEGER,mini_thumb_magic INTEGER," + "bucket_id TEXT,bucket_display_name TEXT,isprivate INTEGER,title_key TEXT," + "artist_id INTEGER,album_id INTEGER,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,is_drm INTEGER," + "width INTEGER, height INTEGER, title_resource_uri TEXT," + "owner_package_name TEXT DEFAULT NULL," + "color_standard INTEGER, color_transfer INTEGER, color_range INTEGER," + "_hash BLOB DEFAULT NULL, is_pending INTEGER DEFAULT 0," + "is_download INTEGER DEFAULT 0, download_uri TEXT DEFAULT NULL," + "referer_uri TEXT DEFAULT NULL, is_audiobook INTEGER DEFAULT 0," + "date_expires INTEGER DEFAULT NULL,is_trashed INTEGER DEFAULT 0," + "group_id INTEGER DEFAULT NULL,primary_directory TEXT DEFAULT NULL," + "secondary_directory TEXT DEFAULT NULL,document_id TEXT DEFAULT NULL," + "instance_id TEXT DEFAULT NULL,original_document_id TEXT DEFAULT NULL," + "relative_path TEXT DEFAULT NULL,volume_name TEXT DEFAULT NULL," + "artist_key TEXT DEFAULT NULL,album_key TEXT DEFAULT NULL," + "genre TEXT DEFAULT NULL,genre_key TEXT DEFAULT NULL,genre_id INTEGER," + "author TEXT DEFAULT NULL, bitrate INTEGER DEFAULT NULL," + "capture_framerate REAL DEFAULT NULL, cd_track_number TEXT DEFAULT NULL," + "compilation INTEGER DEFAULT NULL, disc_number TEXT DEFAULT NULL," + "is_favorite INTEGER DEFAULT 0, num_tracks INTEGER DEFAULT NULL," + "writer TEXT DEFAULT NULL, exposure_time TEXT DEFAULT NULL," + "f_number TEXT DEFAULT NULL, iso INTEGER DEFAULT NULL," + "scene_capture_type INTEGER DEFAULT NULL, generation_added INTEGER DEFAULT 0," + "generation_modified INTEGER DEFAULT 0, xmp BLOB DEFAULT NULL)"); ... db.execSQL("CREATE VIEW images AS SELECT " + String.join(",", getProjectionMap(Images.Media.class).keySet()) + " FROM files WHERE media_type=1"); ...
カラムの構成
テーブルおよびコレクションは次のカラムを持ちます。
データベースの filesテーブル | データベースの imagesテーブル | MediaStoreの imagesコレクション | コメント | |
---|---|---|---|---|
_id | I | _id | 左に同じ | Primary Key Autoinc |
_data | T | _data | Unigue | |
_display_name | T | _display_name | ||
_hash | B | _hash | ||
_size | I | _size | ||
album | T | |||
album_artist | T | |||
album_id | I | |||
artist | T | |||
artist_id | I | |||
bookmark | I | |||
bucket_display_name | T | bucket_display_name | ||
bucket_id | T | bucket_id | ||
category | T | |||
color_range | I | |||
color_standard | I | |||
color_transfer | I | |||
composer | T | |||
date_added | I | date_added | ||
date_expires | I | date_expires | ||
date_modified | I | date_modified | ||
datetaken | I | datetaken | ||
description | T | description | ||
document_id | T | document_id | ||
download_uri | T | |||
duration | I | duration | ||
format | I | |||
group_id | I | group_id | ||
height | I | height | ||
instance_id | T | instance_id | ||
is_alarm | I | 0:false 1:true |
||
is_audiobook | I | |||
is_download | I | |||
is_drm | I | is_drm | ||
is_music | I | |||
is_notification | I | |||
is_pending | I | is_pending | ||
is_podcast | I | |||
is_ringtone | I | |||
is_trashed | I | is_trashed | ||
isprivate | I | isprivate | ||
language | T | |||
latitude | D | latitude | ||
longitude | D | longitude | ||
media_type | I | 1:Image 2:Audio 3:Video |
||
mime_type | T | mime_type | ||
mini_thumb_data | T | |||
mini_thumb_magic | I | mini_thumb_magic | ||
name | T | |||
old_id | I | |||
orientation | I | orientation | ||
original_document_id | T | original_document_id | ||
owner_package_name | T | owner_package_name | ||
parent | I | |||
picasa_id | T | picasa_id | ||
primary_directory | T | primary_directory | ||
referer_uri | T | |||
relative_path | T | relative_path | ||
resolution | T | |||
secondary_directory | T | secondary_directory | ||
tags | T | |||
title | T | title | ||
title_key | T | |||
title_resource_uri | T | |||
track | I | |||
volume_name | T | volume_name | ||
width | I | width | ||
year | I | |||
※カラム名はA-Z順にソート ※I:Integer、R:Real、D:Double、T:Text、B:BLOB(Binary) ※imageコレクション:Media Storeで操作可能なカラム(参照結果のCursorに含まれる) |
データベースの filesテーブル | データベースの imagesテーブル | MediaStoreの imagesコレクション | コメント | |
---|---|---|---|---|
_id | I | _id | 左に同じ | Primary Key Autoinc |
_data | T | _data | Unigue | |
_display_name | T | _display_name | ||
_hash | B | |||
_size | I | _size | ||
album | T | album | ||
album_artist | T | album_artist | ||
album_id | I | |||
album_key | T | |||
artist | T | artist | ||
artist_id | I | |||
artist_key | T | |||
author | T | author | ||
bitrate | I | bitrate | ||
bookmark | I | |||
bucket_display_name | T | bucket_display_name | ||
bucket_id | T | bucket_id | ||
capture_framerate | R | capture_framerate | ||
category | T | |||
cd_track_number | T | cd_track_number | ||
color_range | I | |||
color_standard | I | |||
color_transfer | I | |||
compilation | I | compilation | ||
composer | T | composer | ||
date_added | I | date_added | ||
date_expires | I | date_expires | ||
date_modified | I | date_modified | ||
datetaken | I | datetaken | ||
description | T | description | ||
disc_number | T | disc_number | ||
document_id | T | document_id | ||
download_uri | T | |||
duration | I | duration | ||
exposure_time | T | exposure_time | ||
f_number | T | f_number | ||
format | I | |||
generation_added | I | generation_added | ||
generation_modified | I | generation_modified | ||
genre | T | genre | ||
genre_id | I | |||
genre_key | T | |||
group_id | I | group_id | ||
height | I | height | ||
instance_id | T | instance_id | ||
is_alarm | I | 0:false 1:true |
||
is_audiobook | I | |||
is_download | I | is_download | ||
is_drm | I | is_drm | ||
is_favorite | I | is_favorite | ||
is_music | I | |||
is_notification | I | |||
is_pending | I | is_pending | ||
is_podcast | I | |||
is_ringtone | I | |||
is_trashed | I | is_trashed | ||
iso | I | iso | ||
isprivate | I | isprivate | ||
language | T | |||
latitude | D | latitude | ||
longitude | D | longitude | ||
media_type | I | 1:Images 2:Audio 3:Video |
||
mime_type | T | mime_type | ||
mini_thumb_data | T | |||
mini_thumb_magic | I | mini_thumb_magic | ||
name | T | |||
num_tracks | I | num_tracks | ||
old_id | I | |||
orientation | I | orientation | ||
original_document_id | T | original_document_id | ||
owner_package_name | T | owner_package_name | ||
parent | I | |||
picasa_id | T | picasa_id | ||
primary_directory | T | |||
referer_uri | T | |||
relative_path | T | relative_path | ||
resolution | T | resolution | ||
scene_capture_type | I | scene_capture_type | ||
secondary_directory | T | |||
tags | T | |||
title | T | title | ||
title_key | T | |||
title_resource_uri | T | |||
track | I | |||
volume_name | T | volume_name | ||
width | I | width | ||
writer | T | writer | ||
xmp | B | xmp | ||
year | I | year | ||
※カラム名はA-Z順にソート ※I:Integer、R:Real、D:Double、T:Text、B:BLOB(Binary) ※imageコレクション:Media Storeで操作可能なカラム(参照結果のCursorに含まれる) |
Media Store APIはカラム名をパラメータで定義しています。プログラムは、このパラメータを使います。※詳細は「MediaStore.Images.Media.*」を参照
レコードの構成
レコード(メディア情報)内のフィールドは、次のような関係があります。
- (1)URIを指定すると、_idが定まる
- (2)_idを指定すると、メディア情報が一意に定まる
- (3)メディア情報のrelative_path,_display_nameを指定すると_dataが生成される
- (4)作成された_dataにより、メディアファイルが一意に定まる
(3,4)のrelative_path, _display_nameおよび_dataとファイルの物理的な関係は「固定」です。
関係が崩れると、Media Storeは破綻します。つまり、データを読み出したり、更新したり、といった正常なアクセスが出来なくなります。
関係が崩れないように、relative_path, _display_nameの変更はファイルへ直ちに反映されます。また、この両者から生成される_dataは、変更が禁止されています。
例えば、表示名を連番から具体的な名前(_display_name:「IMG0011.jpg」→「100万ドルの夜景.jpg」)に変更すると、ファイル名も自動的に変更されます。
これに対し、relative_path, _display_nameおよび_data以外とファイルの物理的な関係は「なし」です。
これらのフィールドはメディアファイルのメタ情報であって、関係が崩れたとしても、Media Storeは破綻しません。ただし、論理的な関係は維持しないと、メタ情報の意味がありません。
例えば、MIMEタイプを”image/jpeg”⇒”image/png”にすると、読み出しは問題ありませんが、PNGフォーマットと信じたアプリは表示に失敗します。
関連記事: