「外部ストレージ_プライマリ」に保存されるメディアデータは、Media Storeと呼ばれる特別な方法でアクセスします。
他の方法も可能ですが、Media Storeが最も適した方法です。
この「メディアデータへMedia Storeでアクセス」する方法を、まとめます。
この記事は、Imagesデータ編です。また、基本的なアクセス(読み書き)に的を絞って説明しています。
※環境:Android Studio Narwhal | 2025.1.1 Patch 1
目次
Imagesデータ
メディアデータ
「外部ストレージ_プライマリ」はアプリ間で共有するデータを保存するストレージです。
保存されるデータは、「メディアデータ」「ドキュメント・他のファイル」「アプリ別」に分類されます。
# pwd /storage/emulated # ls -ld 0/* 0/Android/data/com.example.myapp_a/* ↓UserID drwxrwx--x 2 root sdcard_rw 4096 2025-07-31 02:26 0/Alarms drwxrwx--x 3 root sdcard_rw 4096 2025-07-31 02:26 0/Android drwxrwx--x 2 u0_a85 sdcard_rw 4096 2025-07-31 02:30 0/Android/data/com.example.myapp_a/files drwxrwx--x 2 root sdcard_rw 4096 2025-07-31 02:26 0/DCIM drwxrwx--x 2 root sdcard_rw 4096 2025-07-31 02:26 0/Download drwxrwx--x 2 root sdcard_rw 4096 2025-07-31 02:26 0/Movies drwxrwx--x 2 root sdcard_rw 4096 2025-07-31 02:26 0/Music drwxrwx--x 2 root sdcard_rw 4096 2025-07-31 02:26 0/Notifications drwxrwx--x 2 root sdcard_rw 4096 2025-07-31 02:26 0/Pictures drwxrwx--x 2 root sdcard_rw 4096 2025-07-31 02:26 0/Podcasts drwxrwx--x 2 root sdcard_rw 4096 2025-07-31 02:26 0/Ringtones ※エミュレータ(API28)で確認
# pwd /storage/emulated # ls -ld 0/* 0/Android/data/com.example.myapp_a/* ↓UserID drwxrwx--x 2 root sdcard_rw 4096 2025-08-01 13:17 0/Alarms drwxrwx--x 4 root sdcard_rw 4096 2025-08-01 13:17 0/Android drwxrwx--x 2 u0_a146 sdcard_rw 4096 2025-08-01 13:18 0/Android/data/com.example.myapp_a/files drwxrwx--x 2 root sdcard_rw 4096 2025-08-01 13:17 0/DCIM drwxrwx--x 2 root sdcard_rw 4096 2025-08-01 13:17 0/Download drwxrwx--x 2 root sdcard_rw 4096 2025-08-01 13:17 0/Movies drwxrwx--x 2 root sdcard_rw 4096 2025-08-01 13:17 0/Music drwxrwx--x 2 root sdcard_rw 4096 2025-08-01 13:17 0/Notifications drwxrwx--x 2 root sdcard_rw 4096 2025-08-01 13:17 0/Pictures drwxrwx--x 2 root sdcard_rw 4096 2025-08-01 13:17 0/Podcasts drwxrwx--x 2 root sdcard_rw 4096 2025-08-01 13:17 0/Ringtones ※エミュレータ(API29)で確認
# pwd /storage/emulated # ls -ld 0/* 0/Android/data/com.example.myapp_a/* ↓UserID drwx------ 2 u0_a163 u0_a163 4096 2025-08-01 13:21 0/Alarms drwxrws--x 5 media_rw media_rw 4096 2025-08-01 13:21 0/Android drwxrws--- 2 u0_a167 ext_data_rw 4096 2025-08-01 13:21 0/Android/data/com.example.myapp_a/files drwx------ 2 u0_a163 u0_a163 4096 2025-08-01 13:21 0/Audiobooks drwx------ 2 u0_a163 u0_a163 4096 2025-08-01 13:21 0/DCIM drwx------ 2 u0_a163 u0_a163 4096 2025-08-01 13:21 0/Documents drwx------ 2 u0_a163 u0_a163 4096 2025-08-01 13:21 0/Download drwx------ 3 u0_a163 u0_a163 4096 2025-08-01 13:21 0/Movies drwx------ 3 u0_a163 u0_a163 4096 2025-08-01 13:21 0/Music drwx------ 2 u0_a163 u0_a163 4096 2025-08-01 13:21 0/Notifications drwx------ 3 u0_a163 u0_a163 4096 2025-08-01 13:21 0/Pictures drwx------ 2 u0_a163 u0_a163 4096 2025-08-01 13:21 0/Podcasts drwx------ 2 u0_a163 u0_a163 4096 2025-08-01 13:21 0/Ringtones ※エミュレータ(API30)で確認
# pwd /storage/emulated # ls -ld 0/* 0/Android/data/com.example.myapp_a/* ↓UserID drwxrws--- 2 u0_a163 media_rw 4096 2025-08-01 13:23 0/Alarms drwxrws--x 5 media_rw media_rw 4096 2025-08-01 13:23 0/Android drwxrws--- 2 u0_a174 ext_data_rw 4096 2025-08-01 13:23 0/Android/data/com.example.myapp_a/files drwxrws--- 2 u0_a163 media_rw 4096 2025-08-01 13:23 0/Audiobooks drwxrws--- 2 u0_a163 media_rw 4096 2025-08-01 13:23 0/DCIM drwxrws--- 2 u0_a163 media_rw 4096 2025-08-01 13:23 0/Documents drwxrws--- 2 u0_a163 media_rw 4096 2025-08-01 13:23 0/Download drwxrws--- 3 u0_a163 media_rw 4096 2025-08-01 13:23 0/Movies drwxrws--- 3 u0_a163 media_rw 4096 2025-08-01 13:23 0/Music drwxrws--- 2 u0_a163 media_rw 4096 2025-08-01 13:23 0/Notifications drwxrws--- 3 u0_a163 media_rw 4096 2025-08-01 13:23 0/Pictures drwxrws--- 2 u0_a163 media_rw 4096 2025-08-01 13:23 0/Podcasts drwxrws--- 2 u0_a163 media_rw 4096 2025-08-01 13:23 0/Recordings drwxrws--- 2 u0_a163 media_rw 4096 2025-08-01 13:23 0/Ringtones ※エミュレータ(API33)で確認
アンドロイドの標準アプリ(カメラ、画像ビューアー、音楽・動画プレーヤー)の扱うデータ(画像、音楽、動画)がメディアデータです。
Imagesテーブル
メディアデータはデータベースで管理されます。
そして、効率よく管理できるように、メディアのデータタイプ毎にテーブルが設けられています。
- Imagesテーブル
- Videoデーブル
- Audioテーブル
この中のImagesテーブルは、「カメラで撮影した写真」、「ポスター・イラストといったアート作品」、「動画のスナップショット」など、画像全般を収集したものです。
※データベースの詳細は「Imagesデータのデータベースによる管理(API≧29)」を参照
Media Store
メディアデータはMedia Storeと呼ばれる特別なアクセス方法が準備されています。
Content Resolver/Provider経由で、外部ストレージに保存されるメディアデータへ、アクセスする方法です。
※Content Providerについては「App Component:Content Provider」を参照
Media Store APIを使えば、データベース上のメディア情報を参照して、要求したImagesの一覧(imegesコレクション)を一括で取得できます。大変便利です。
Media Storeはメディアデータへアクセスするために最も適した方法と言えます。
パーミッションの取得
メディアデータはアプリ間で共有されます。
アプリとデータのプライバシー保護の観点から、アクセスするアプリはユーザからパーミッション(許可)を取得しなければなりません。
そして、許可された権限の範囲でメディアデータへアクセスできます。
※詳細は「外部ストレージへアクセスするパーミッションと権限の範囲」を参照
アクセスの基本
Media Storeを使ったアクセスの基本を示します。
一切のオプション的な動作を排除し、最も簡素に行った場合です。単に書き込み・読み出しを行います。
メディア情報の追加
データベースのテーブルへ画像情報(メディア情報)を追加する方法は、次の通りです。
private val ContentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI fun Context.insertImage(data: ByteArray): Uri? { val _contentValues = ContentValues() val _mediaUri = contentResolver.insert(ContentUri, _contentValues) _mediaUri?.let { writeMediaFile(data, _mediaUri) } return _mediaUri // メディア情報のURI }
insertが実行されると、表のようなメディア情報が作成されて、そのレコードがデータベースへ登録されます。この時、ContentValueが空であれば、デフォルト値が使われます。
カラム | 意味 | デフォルト値 |
---|---|---|
_id | プライマリキー | ユニークな連番 |
_data | ファイルのパス | relative_pathと_display_nameから自動生成 |
_display_name | 表示名 | 追加時の時刻(ミリ秒)+".jpg" |
mime_type | マイムタイプ | JPEGフォーマット |
bucket_display_name | バケット名 | Pictures(格納フォルダ名) |
bucket_id | バケットID | 格納フォルダのパスのハッシュ値(※) |
relative_path | 格納フォルダ | Pictures/ |
volume_name | 記録領域名 | external_primary |
owner_package_name | 所有者 | アプリのパッケージ名 |
title | タイトル | 追加時の時刻(ミリ秒) |
※ハッシュ値の計算方法:「"/storage/emulated/0/Pictures".lowercase().hashCode()」 |
_dataは「relative_pathと_display_nameから自動生成」されるので、注意して下さい。relative_pathと_display_nameがデフォルト値ならば、デフォルト値を使った_dataになります。
# sqlite3 ./external.db sqlite> .headers on sqlite> .mode column sqlite> select _id,_data,_display_name,mime_type,bucket_display_name,bucket_id,title from images; _id _data _display_name mime_type bucket_display_name bucket_id relative_path volume_name owner_package_name title ---------- ---------------------------------------------- ----------------- ---------- ------------------- ----------- ------------- ---------------- ------------------------------ ------------- 24 /storage/emulated/0/Pictures/1749628360215.jpg 1749628360215.jpg image/jpeg Pictures -1617409521 Pictures/ external_primary com.example.xxxxxxxxxxxxxxxxxx 1749628360215
そして、メディア情報のURI(insertの戻り値)が確定します。
メディアファイルの書き込み
メディア情報のURIが確定していれば、書き込みはこのURIを使って行います。
fun Context.writeMediaFile(data: ByteArray, uri: Uri) { try { contentResolver.openOutputStream(uri, "w")?.use { outputStream -> outputStream.write(data) outputStream.flush() } } catch (e: Exception) { Log.i(TAG, e.toString()) } }
openOutputStreamが実行されると、URIが示すレコードがデータベースから読み出されます。そして、メディア情報の_dataフィールドが参照されて、指定されたファイルパスにデータを出力します。
val _imageData = raw2byteArray(this, R.raw.donguri) val _uri = insertImage(_imageData)
# pwd /storage/emulated/0 ← 外部ストレージ # ls -ld Pictures/* -rw-rw---- 1 root sdcard_rw 54206 2025-06-11 07:52 Pictures/1749628360215.jpg
メディアファイルの読み出し
メディア情報のURIが確定していれば、読み出しはこのURIを使って行います。
fun Context.readMediaFile(uri: Uri): ByteArray { val _os = ByteArrayOutputStream() contentResolver.openInputStream(uri).use { stream -> _os.use { stream?.copyTo(_os) } } return _os.toByteArray() }
openInputStreamが実行されると、URIが示すレコードがデータベースから読み出されます。そして、メディア情報の_dataフィールドが参照されて、指定されているパスからデータを取得します。
val _data = _uri?.let { readMediaFile(it) } Log.i(TAG, "Data = ${_data?.take(10)}")
Data = [-1, -40, -1, -32, 0, 16, 74, 70, 73, 70]
Media Storeで指定するURI
Media Storeで指定するURIの表現方法は、大きく分けて2つあります。
val _uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 77) Log.i(TAG, "Uri(Images Table) = ${MediaStore.Images.Media.EXTERNAL_CONTENT_URI}") Log.i(TAG, "Uri(Video Table) = ${MediaStore.Video.Media.EXTERNAL_CONTENT_URI}") Log.i(TAG, "Uri(Audio Table) = ${MediaStore.Audio.Media.EXTERNAL_CONTENT_URI}") Log.i(TAG, "Uri(Files Table) = ${MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)}") Log.i(TAG, "Uri(Media Info) = ${_uri}")
Uri(Images Table) = content://media/external/images/media Uri(Video Table) = content://media/external/video/media Uri(Audio Table) = content://media/external/audio/media Uri(Files Table) = content://media/external/file Uri(Media Info) = content://media/external/images/media/77content://media/external/images/media
imagesテーブルを指定するURIです。
content://media/external/images/media/ID番号メディア情報を一意に指定するURIです。
操作対象を絞り込む際に、使い分けます。
ContentUrisクラスに便利なユーティリティが定義されているので助かります。
/** * メディア情報を一意に指定するURIの合成 */ val _uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 77) /** * IDを取得 */ Log.i(TAG, "Uri = ${_uri} ID = ${ContentUris.parseId(_uri)}") /** * テーブルを指定するURIの取得(API≧29) */ Log.i(TAG, "Uri = ${_uri} NewUri = ${ContentUris.removeId(_uri)}")
Uri = content://media/external/images/media/77 ID = 77 Uri = content://media/external/images/media/77 NewUri = content://media/external/images/media
関連記事: