ImagesデータへMedia Storeでアクセス(API≦28)

投稿日:  更新日:

「外部ストレージ_プライマリ」に保存されるメディアデータは、Media Storeと呼ばれる特別な方法でアクセスします。

他の方法も可能ですが、Media Storeが最も適した方法です。

この「メディアデータへMedia Storeでアクセス」する方法を、まとめます。

この記事は、Imagesデータ編です。また、基本的なアクセス(読み書き)に的を絞って説明しています。

※環境:Android Studio Narwhal | 2025.1.1 Patch 1

スポンサーリンク

Imagesデータ

メディアデータ

「外部ストレージ_プライマリ」はアプリ間で共有するデータを保存するストレージです。

保存されるデータは、「メディアデータ」「ドキュメント・他のファイル」「アプリ別」に分類されます。

外部ストレージのフォルダ

API≦28API=29API=30~32API≧33
# 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データの構成

そして、効率よく管理できるように、メディアのデータタイプ毎にテーブルが設けられています。

  • Imagesテーブル
  • Videoデーブル
  • Audioテーブル

この中のImagesテーブルは、「カメラで撮影した写真」、「ポスター・イラストといったアート作品」、「動画のスナップショット」など、画像全般を収集したものです。

※データベースの詳細は「Imagesデータのデータベースによる管理(API≦28)」を参照

スポンサーリンク

Media Store

メディアデータはMedia Storeと呼ばれる特別なアクセス方法が準備されています。

Content Resolver/Provider経由で、外部ストレージに保存されるメディアデータへ、アクセスする方法です。
※Content Providerについては「App Component:Content Provider」を参照

Media Storeの概要

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ファイルのパスデフォルト値から自動生成
 格納フォルダ名:Pictures
 ファイル名  :追加時の時刻(ミリ秒)+".jpg"
_display_name表示名追加時の時刻(ミリ秒)+".jpg"
mime_typeマイムタイプJPEGフォーマット
bucket_display_nameバケット名Pictures(格納フォルダ名)
bucket_idバケットID格納フォルダのパスのハッシュ値(※)
titleタイトル追加時の時刻(ミリ秒)
※ハッシュ値の計算方法:「"/storage/emulated/0/Pictures".lowercase().hashCode()」

_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    title
----------  ----------------------------------------------  -----------------  ----------  -------------------  -----------  -------------
26          /storage/emulated/0/Pictures/1749088325155.jpg  1749088325155.jpg  image/jpeg  Pictures             -1617409521  1749088325155

そして、メディア情報の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)
raw2byteArray
fun raw2byteArray(context: Context, @RawRes id: Int): ByteArray {
    val _is = context.resources.openRawResource(id)
    val _os = ByteArrayOutputStream()
    _is.use {
        _os.use {
            _is.copyTo(_os)
        }
    }
    return _os.toByteArray()
}
# pwd
/storage/emulated/0				← 外部ストレージ
# ls -ld Pictures/*
-rw-rw---- 1 root sdcard_rw 54206 2025-06-05 01:52 Pictures/1749088325155.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/77
 content://media/external/images/media 

テーブル全体を指定する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
スポンサーリンク

関連記事:

ストレージのリソースは、内部メモリー、外部メモリー、クラウドの3つがあります。 外部メモリーは、主にSDカードです。 このSDカードはAndroid端末の世代が進むにつれて、扱いを変えてきました。 リソースとSDカードを主眼に置いたストレージの変遷について、まとめます。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
Adoptable Storage(API≧23、Android 6.0)が導入されて、SDカードの扱い方をユーザ側で指定できるようになりました。 「Adoptable Storage」について、まとめます。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
ストレージはデータの用途別に記憶領域が分けられます。 保存先を守らないと、セキュリティリスクが発生したり、他のアプリと協調した動作が出来なくなったり、します。 ですので、適切な場所へデータを保存しましょう。 今回は「用途別記憶領域とボリューム」について、まとめます。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
ストレージへアクセスする方法は、「扱うデータの種類」「アクセス先」「セキュリティの確保」などの要件により、最適なアクセス方法が存在するので、使い分けが必要です。 今回は「アクセスする方法」について、まとめます。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
「内部ストレージ」にアクセスする方法について、まとめます。 「内部ストレージ」は、セキュリティが高いうえに、端末の内部メモリーに構築されるので、必ず存在します。 使いたい時に安心して使え、もっとも使い勝手のよいストレージと言えます。 アプリのデータを保存したいならば、保存先として真っ先に考えるストレージです。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
外部ストレージへアクセスするアプリは、パーミッション(許可)をユーザーから取得する必要があります。 パーミッションを得たアプリは、許可された権限の範囲内で、ストレージへアクセスが可能です。 これらは、ユーザーデータのプライバシー保護と密接に関係しています。 プライバシー保護をより強固にするために、パーミッションと権限の範囲の仕様は改変されてきました。 今回は、改変の歴史を辿りつつ、「パーミッションと権限の範囲」について、まとめます。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
「外部ストレージ_プライマリ」にアクセスする方法について、まとめます。 「外部ストレージ_プライマリ」は、アプリ間で共有するデータを保存するストレージです。 主にカメラ、音楽プレーヤー、動画プレーヤーで扱うデータを保存します。 「外部ストレージ_プライマリ」に保存したデータは、他のアプリに公開することになるので、秘匿性の高いデータの保存に適しません。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
「外部ストレージ_セカンダリ」にアクセスする方法について、まとめます。 「外部ストレージ_セカンダリ」は、デバイス間(または、アプリ間)で共有するデータを保存するストレージです。 デバイスとは、他の携帯端末やパソコンなどを指します。 ストレージがSDカードのような取り外し可能なリソース上に構築されているので、データの共有は取り外したストレージを他のデバイスへ取り付けて行います。 「外部ストレージ_セカンダリ」に保存したデータは、他のデバイスに公開することになるので、秘匿性の高いデータの保存に適しません。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
メディアデータはデータベースによって管理されています。 Media Store APIを使ってメディアデータへアクセスしているのであれば、データベースの所在や構成などを意識する必要はありません。 ただ、「より複雑な制御をしたい場合」や「デバックを効率化したい場合」などに、データベースの知識が役立ちます。 今回は「メディアデータのデータベースによる管理」について、まとめます。 この記事は、Imagesデータ編です。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
「外部ストレージ_プライマリ」に保存されるメディアデータは、Media Storeと呼ばれる特別な方法でアクセスします。 他の方法も可能ですが、Media Storeが最も適した方法です。 この「メディアデータへMedia Storeでアクセス」する方法を、まとめます。 この記事は、Imagesデータ編です。また、基本的なアクセス(読み書き)に的を絞って説明しています。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
メディアデータはデータベースによって管理されています。 Media Store APIを使ってメディアデータへアクセスしているのであれば、データベースの所在や構成などを意識する必要はありません。 ただ、「より複雑な制御をしたい場合」や「デバックを効率化したい場合」などに、データベースの知識が役立ちます。 今回は「メディアデータのデータベースによる管理」について、まとめます。 この記事は、Imagesデータ編です。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
スポンサーリンク