メディアデータへMedia Storeで追加・参照・更新・削除

投稿日:  更新日:

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

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

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

その記事は、基本的なアクセスとして「追加(insert)&読み書き」のみを説明しています。

この記事は、上記に加えて「参照(query)・更新(update)・削除(delete)」を説明します。

※環境:Android Studio Narwhal | 2025.1.1 Patch 1

スポンサーリンク

追加(insert)

以下の記事を参照
ImagesデータへMedia Storeでアクセス(API≦28)
ImagesデータへMedia Storeでアクセス(API≧29)

スポンサーリンク

参照(query)

データベースを参照して、管理されているメディア情報の一覧を取得します。

ContentResolver#queryを用います。戻り値はCursorオブジェクトです。

Cursorオブジェクトは扱いにくいので、このサンプルはMediaDataのリストへ変換しています。

data class MediaData(val id: Long, val filename: String?, val filepath: String? = null)
//
fun Context.queryMedia(uri: Uri): List<MediaData> {
    val _list = mutableListOf<MediaData>()

    val _projection = null       // 取得するフィールド
    val _selection = null        // 検索条件
    val _selectionArgs = null    // 検索条件の値
    val _sortOrder = null        // ソート順

    try {
        contentResolver.query(
            uri,
            _projection, _selection, _selectionArgs, _sortOrder
        )?.use { cursor ->
            _list.parse(cursor)
        }
    } catch (e: Exception) { Log.i(TAG, e.toString()) }

    return _list.toList()
}
//
_list.parse()
fun MutableList<MediaData>.parse(cursor: Cursor) {
    Log.i(TAG, "Cursor size = ${cursor.count}")
    val _idIndex = cursor.getColumnIndexOrThrow(IdKey)
    val _fileNameIndex = cursor.getColumnIndexOrThrow(DisplayNameKey)
    val _dataIndex = cursor.getColumnIndexOrThrow(DataKey)
    while (cursor.moveToNext()) {
        val _id = cursor.getLong(_idIndex)
        val _filename = cursor.getString((_fileNameIndex))
        val _filepath = cursor.getString(_dataIndex)

        add(MediaData(_id, _filename, _filepath))
    }
}
//

サンプルは以下の状態を元に実行した結果です。

# pwd
/data/data/com.android.providers.media/databases
# sqlite3 ./external.db
sqlite> .headers on
sqlite> .mode column
sqlite> select _id,_display_name,_data from images;
_id         _display_name      _data
----------  -----------------  ----------------------------------------------
34          1754637551813.jpg  /storage/emulated/0/Pictures/1754637551813.jpg
35          1754637551833.jpg  /storage/emulated/0/DCIM/1754637551833.jpg
36          1754637551840.jpg  /storage/emulated/0/Pictures/1754637551840.jpg
37          1754637551845.jpg  /storage/emulated/0/DCIM/1754637551845.jpg
スポンサーリンク

テーブル全体

                                val _contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
                                val _list = queryMedia(_contentUri)
                                _list.dump(TAG)
								//
_list.dump()
fun List<MediaData>.dump(tag: String) {
    Log.i(tag, "List size = ${size}")
    forEach { Log.i(tag, it.toString()) }
}
//
List size = 4
MediaData(id=34, filename=1754637551813.jpg, filepath=/storage/emulated/0/Pictures/1754637551813.jpg)
MediaData(id=35, filename=1754637551833.jpg, filepath=/storage/emulated/0/DCIM/1754637551833.jpg)
MediaData(id=36, filename=1754637551840.jpg, filepath=/storage/emulated/0/Pictures/1754637551840.jpg)
MediaData(id=37, filename=1754637551845.jpg, filepath=/storage/emulated/0/DCIM/1754637551845.jpg)

任意のURI

                                val _contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
								val _id = 34L
                                val _uri = ContentUris.withAppendedId(_contentUri, _id)
                                val _list = queryMedia(_uri)
                                _list.dump(TAG)
List size = 1
MediaData(id=34, filename=1754637551813.jpg, filepath=/storage/emulated/0/Pictures/1754637551813.jpg)
スポンサーリンク

更新(update)

データベースを参照して、管理されているメディア情報を更新します。

ContentResolver#updateを用います。戻り値は更新が成功した数です。

レコードの一部のフィールドは、ファイルとの物理的な関係が「固定」になっています。更新すると、Media Storeが破綻したり、ファイル名やバケット名に影響を与えたり、します。注意して下さい。

詳細は以下を参照
Imagesデータのデータベースによる管理(API≦28)
Imagesデータのデータベースによる管理(API≧29)

fun Context.updateMedia(uri: Uri, title: String): Int {
    val _selection = null        // 検索条件
    val _selectionArgs = null    // 検索条件の値
    val _contentValues = ContentValues().apply {
        put(TitleKey, title)
    }

    var _num = 0
    try {
        _num = contentResolver.update(
            uri,
            _contentValues,
            _selection, _selectionArgs
        )
    } catch (e: Exception) { Log.i(TAG, e.toString()) }

    return _num
}

サンプルは以下の状態を元に実行した結果です。

# pwd
/data/data/com.android.providers.media/databases
# qlite3 ./external.db                                                                                     
sqlite> .headers on
sqlite> .mode column
sqlite> select _id,_display_name,bucket_display_name,title from images;
_id         _display_name      bucket_display_name  title
----------  -----------------  -------------------  -------------
38          1754637849272.jpg  Pictures             1754637849272
39          1754637849282.jpg  DCIM                 1754637849282
40          1754637849286.jpg  Pictures             1754637849286
41          1754637849294.jpg  DCIM                 1754637849294
スポンサーリンク

テーブル全体

                                val _contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
                                val _num = updateMedia(_contentUri, "Birthday")
                                Log.i(TAG, "Update number = ${_num}")
Update number = 4
sqlite> select _id,_display_name,bucket_display_name,title from images;
_id         _display_name      bucket_display_name  title
----------  -----------------  -------------------  ----------
38          1754637849272.jpg  Pictures             Birthday
39          1754637849282.jpg  DCIM                 Birthday
40          1754637849286.jpg  Pictures             Birthday
41          1754637849294.jpg  DCIM                 Birthday

任意のURI

                                val _contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
								val _id = 38L
                                val _uri = ContentUris.withAppendedId(_contentUri, _id)
                                val _num = updateMedia(_uri, "Birthday")
                                Log.i(TAG, "Update number = ${_num}")
Update number = 1
sqlite> select _id,_display_name,bucket_display_name,title from images;
_id         _display_name      bucket_display_name  title
----------  -----------------  -------------------  ----------
38          1754637849272.jpg  Pictures             Birthday
39          1754637849282.jpg  DCIM                 1754637849
40          1754637849286.jpg  Pictures             1754637849
41          1754637849294.jpg  DCIM                 1754637849
スポンサーリンク

削除(delete)

データベースを参照して、管理されているメディア情報を削除します。

ContentResolver#deleteを用います。戻り値は削除が成功した数です。

メディア情報が削除されると、メディア情報の_data(ファイルバス)が示すファイルも削除されます。

fun Context.deleteMedia(uri: Uri): Int {
    val _selection = null        // 検索条件
    val _selectionArgs = null    // 検索条件の値

    var _num = 0
    try {
        _num = contentResolver.delete(
            uri,
            _selection, _selectionArgs
        )
    } catch (e: Exception) { Log.i(TAG, e.toString()) }

    return _num
}

サンプルは以下の状態を元に実行した結果です。

# pwd
/data/data/com.android.providers.media/databases
# qlite3 ./external.db                                                                                     
sqlite> .headers on
sqlite> .mode column
sqlite> select _id,_display_name,bucket_display_name,title from images;
_id         _display_name      bucket_display_name  title
----------  -----------------  -------------------  -------------
46          1754638782351.jpg  Pictures             1754638782351
47          1754638782359.jpg  DCIM                 1754638782359
48          1754638782363.jpg  Pictures             1754638782363
49          1754638782372.jpg  DCIM                 1754638782372
# pwd
/storage/emulated/0
# ls -ld */*
-rw-rw---- 1 root sdcard_rw 47891 2025-08-08 07:39 DCIM/1754638782359.jpg
-rw-rw---- 1 root sdcard_rw 47891 2025-08-08 07:39 DCIM/1754638782372.jpg
-rw-rw---- 1 root sdcard_rw 47891 2025-08-08 07:39 Pictures/1754638782351.jpg
-rw-rw---- 1 root sdcard_rw 47891 2025-08-08 07:39 Pictures/1754638782363.jpg
スポンサーリンク

テーブル全体

                                val _contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
                                val _num = deleteMedia(_contentUri)
                                Log.i(TAG, "Delete number = ${_num}")
Delete number = 4
sqlite> select _id,_display_name,bucket_display_name,title from images;
※表示なし
# pwd
/storage/emulated/0
# ls -ld */*
※表示なし

任意のURI

                                val _contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
                                // val _contentUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL)
								val _id = 46L
                                val _uri = ContentUris.withAppendedId(_contentUri, _id)
                                val _num = deleteMedia(_uri)
                                Log.i(TAG, "Delete number = ${_num}")
Delete number = 1
sqlite> select _id,_display_name,bucket_display_name,title from images;
_id         _display_name      bucket_display_name  title
----------  -----------------  -------------------  -------------
47          1754638782359.jpg  DCIM                 1754638782359
48          1754638782363.jpg  Pictures             1754638782363
49          1754638782372.jpg  DCIM                 1754638782372
# pwd
/storage/emulated/0
# ls -ld */*
-rw-rw---- 1 root sdcard_rw 47891 2025-08-08 07:39 DCIM/1754638782359.jpg
-rw-rw---- 1 root sdcard_rw 47891 2025-08-08 07:39 DCIM/1754638782372.jpg
-rw-rw---- 1 root sdcard_rw 47891 2025-08-08 07:39 Pictures/1754638782363.jpg
スポンサーリンク

関連記事:

ストレージのリソースは、内部メモリー、外部メモリー、クラウドの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と呼ばれる特別な方法でアクセスします。 他の方法も可能ですが、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 ...
「外部ストレージ_プライマリ」に保存されるメディアデータは、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 ...
「外部ストレージ_プライマリ」に保存されるメディアデータは、データベースで管理されます。 Media Storeを使うと、このデータベースを通してメディアデータへアクセスできます。 データベース(SQLite)が持つ検索機能を使ってアクセス対象を絞り込めるので、とても便利です。 この記事は、「絞り込アクセス」について、まとめます。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
スポンサーリンク