「外部ストレージ_プライマリ」に保存されるメディアデータは、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()
}
//
サンプルは以下の状態を元に実行した結果です。
# 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 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
関連記事:
