メディアデータへMedia Storeで絞り込アクセス

投稿日:  更新日:

「外部ストレージ_プライマリ」に保存されるメディアデータは、データベースで管理されます。

Media Storeを使うと、このデータベースを通してメディアデータへアクセスできます。

データベース(SQLite)が持つ検索機能を使ってアクセス対象を絞り込めるので、とても便利です。

この記事は、「絞り込アクセス」について、まとめます。

※環境:Android Studio Narwhal | 2025.1.1 Patch 1

スポンサーリンク

絞り込みアクセス

メディアデータへMedia Storeでアクセスする際に、次のコマンドが使われます。

  • ContentResolver#query … メディア情報の参照
  • ContentResolver#update … メディア情報の更新
  • ContentResolver#delete … メディア情報の削除

これらのコマンドは、アクセス対象のメディア情報を絞り込む機能があります。

queryupdatedelete
    @Nullable
    public final Cursor query(
	    @NonNull Uri uri,
	    @Nullable String[] projection, 		// 取得するカラム
	    @Nullable String selection, 		// 検索条件
	    @Nullable String[] selectionArgs, 	// 検索条件の値
	    @Nullable String sortOrder			// ソート順
	) { ... }
    public final int update(
	    @NonNull Uri uri, 
	    @Nullable ContentValues values, 
	    @Nullable String where, 			// 検索条件(selectionと同じ)
	    @Nullable String[] selectionArgs	// 検索条件の値
	) { ... }
    public final int delete(
	    @NonNull Uri url, 
	    @Nullable String where,  			// 検索条件(selectionと同じ)
	    @Nullable String[] selectionArgs	// 検索条件の値
	) { ... }

そして、絞り込みの条件は引数で指定します。

(1)projection引数は、アクセスするカラムを絞り込み ※queryのみ
(2)selection/selectionArgs引数は、カラム値が条件に合うメディア情報を絞り込み

スポンサーリンク

(1)projection(queryのみ)

projectionへアクセス対象のカラム名をString配列で指定します。

queryにおいて、結果(Cursorオブジェクト)が指定したカラムに限定されます。

# pwd
/data/data/com.android.providers.media/databases
# sqlite3 ./external.db
sqlite> .headers on
sqlite> .mode column
sqlite> select * from images;
_id         _data                                           _size       _display_name      mime_type   title          date_added  date_modified  description  picasa_id   isprivate   latitude    longitude   datetaken   orientation  mini_thumb_magic  bucket_id    bucket_display_name  width       height
----------  ----------------------------------------------  ----------  -----------------  ----------  -------------  ----------  -------------  -----------  ----------  ----------  ----------  ----------  ----------  -----------  ----------------  -----------  -------------------  ----------  ----------
26          /storage/emulated/0/Pictures/1754872524603.jpg              1754872524603.jpg  image/jpeg  1754872524603  1754872524                                                                                                                         -1617409521  Pictures
27          /storage/emulated/0/DCIM/1754872524618.jpg                  1754872524618.jpg  image/jpeg  1754872524618  1754872524                                                                                                                         -2075821635  DCIM
28          /storage/emulated/0/Pictures/1754872524622.png              1754872524622.png  image/png   1754872524622  1754872524                                                                                                                         -1617409521  Pictures
29          /storage/emulated/0/DCIM/1754872524628.png                  1754872524628.png  image/png   1754872524628  1754872524                                                                                                                         -2075821635  DCIM
                                // val _projection = null		// 取得するカラム(※1)
                                val _projection = arrayOf(	 	// 取得するカラム
                                    MediaStore.Images.Media._ID,
                                    MediaStore.Images.Media.DISPLAY_NAME
                                )
                                val _selection = null        	// 検索条件
                                val _selectionArgs = null    	// 検索条件の値
                                val _sortOrder = null        	// ソート順

                                val _cursor = contentResolver.query(
                                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                    _projection,
                                    _selection,
                                    _selectionArgs,
                                    _sortOrder
                                )

                                _cursor?.use { dumpCursor(it) }
補助関数:dumpCursor( )
                                fun dumpCursor(cursor: Cursor) {
                                    Log.i(TAG, "Column names = ${cursor.columnNames.toList()}")
                                    while (cursor.moveToNext()) {
                                        val _index = cursor.getColumnIndex(MediaStore.MediaColumns._ID)
                                        val _id = cursor.getLong(_index)
                                        Log.i(TAG, "Media id = ${_id}")
                                    }
                                }
Column names = [_id, _display_name]
Media id = 26
Media id = 27
Media id = 28
Media id = 29

ちなみに、nullを指定(※1)すると、絞り込みは無効になり、すべてのカラムがアクセス対象になります。

Column names = [_id, _data, _size, _display_name, mime_type, title, date_added, date_modified, description, picasa_id, isprivate, latitude, longitude, datetaken, orientation, mini_thumb_magic, bucket_id, bucket_display_name, width, height]
Media id = 26
Media id = 27
Media id = 28
Media id = 29
スポンサーリンク

(2)selection/selectionArgs

カラム値が条件に合うロウ(メディア情報)をアクセス対象にします。SQLの「WHERE句」と同じです。

selectionへ条件式を指定します。この条件式に「?」があると、「?」はselectArgsの値に置き換わります。

selectArgsは条件値をString配列で指定します。

  • 左から1番目の「?」 ← selectArgsの[0]
  • 左から2番目の「?」 ← selectArgsの[1]
  • 左から3番目の「?」 ← selectArgsの[2]
# pwd
/data/data/com.android.providers.media/databases
# sqlite3 ./external.db
sqlite> .headers on
sqlite> .mode column
sqlite> select _id,_display_name,mime_type from images;
_id         _display_name      mime_type
----------  -----------------  ----------
26          1754872524603.jpg  image/jpeg
27          1754872524618.jpg  image/jpeg
28          1754872524622.png  image/png
29          1754872524628.png  image/png
                                val _projection = null       // 取得するカラム
                                // val _selection = null     // 検索条件(※2)
                                // val _selectionArgs = null // 検索条件の値
                                val _selection = "${MediaStore.Images.Media.MIME_TYPE}=?"
                                val _selectionArgs = arrayOf(
                                    "image/png"
                                )
                                val _sortOrder = null        // ソート順

                                val _cursor = contentResolver.query(
                                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                    _projection,
                                    _selection,
                                    _selectionArgs,
                                    _sortOrder
                                )

                                _cursor?.use { dumpCursor(it) }
Column names = [_id, _data, _size, _display_name, mime_type, title, date_added, date_modified, description, picasa_id, isprivate, latitude, longitude, datetaken, orientation, mini_thumb_magic, bucket_id, bucket_display_name, width, height]
Media id = 28		... PNG
Media id = 29

ちなみに、nullを指定(※2)すると、絞り込みは無効になり、すべてのロウ(メディア情報)がアクセス対象になります。

Column names = [_id, _data, _size, _display_name, mime_type, title, date_added, date_modified, description, picasa_id, isprivate, latitude, longitude, datetaken, orientation, mini_thumb_magic, bucket_id, bucket_display_name, width, height]
Media id = 26		... JPEG
Media id = 27
Media id = 28		... PNG
Media id = 29
スポンサーリンク

例:id(複数)を持つメディア情報

複数のidを指定し、そのidを持つメディア情報へ絞り込む例です。

複数の条件式を「or」で連結することも出来ますが、idの個数が可変なら「in」の利用が得策です。

sqlite> select _id,_display_name,mime_type from images;
_id         _display_name      mime_type
----------  -----------------  ----------
26          1754872524603.jpg  image/jpeg
27          1754872524618.jpg  image/jpeg
28          1754872524622.png  image/png
29          1754872524628.png  image/png
30          1754879401184.jpg  image/jpeg
31          1754879401204.jpg  image/jpeg
32          1754879401210.png  image/png
33          1754879401215.png  image/png
                                val _ids = "'27','29','32'"	 // id(複数)
								
                                val _projection = null       // 取得するカラム
                                val _selection = "${MediaStore.Images.Media._ID} in (${_ids})"
                                val _selectionArgs = null
                                val _sortOrder = null        // ソート順

                                val _cursor = contentResolver.query(
                                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                    _projection,
                                    _selection,
                                    _selectionArgs,
                                    _sortOrder
                                )

                                _cursor?.use { dumpCursor(it) }
Column names = [_id, _data, _size, _display_name, mime_type, title, date_added, date_modified, description, picasa_id, isprivate, latitude, longitude, datetaken, orientation, mini_thumb_magic, bucket_id, bucket_display_name, width, height]
Media id = 27
Media id = 29
Media id = 32
スポンサーリンク

例:nullを持つメディア情報

SQLiteにおいて、値が未指定のフィールドはnullです。

nullを持つメディア情報へ絞り込む例です。

sqlite> select _id,_display_name,mime_type,isprivate from images;
_id         _display_name      mime_type   isprivate
----------  -----------------  ----------  ----------
26          1754872524603.jpg  image/jpeg              ... isprivate:null
27          1754872524618.jpg  image/jpeg  0           ... ispribate:false
28          1754872524622.png  image/png
29          1754872524628.png  image/png
30          1754879401184.jpg  image/jpeg  0
31          1754879401204.jpg  image/jpeg  0
32          1754879401210.png  image/png
33          1754879401215.png  image/png   0

※isprivateはBoolean、0:false/1:true(正しくは>0)
                                val _projection = null       // 取得するカラム
                                val _selection = "${MediaStore.Images.Media.IS_PRIVATE} is null"
                                val _selectionArgs = null
                                val _sortOrder = null        // ソート順

                                val _cursor = contentResolver.query(
                                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                    _projection,
                                    _selection,
                                    _selectionArgs,
                                    _sortOrder
                                )

                                _cursor?.use { dumpCursor(it) }
Column names = [_id, _data, _size, _display_name, mime_type, title, date_added, date_modified, description, picasa_id, isprivate, latitude, longitude, datetaken, orientation, mini_thumb_magic, bucket_id, bucket_display_name, width, height]
Media id = 26
Media id = 28
Media id = 29
Media id = 32
スポンサーリンク

例:範囲内のメディア情報

昨日追加されたメディア情報へ絞り込む例です。

上限と下限の条件式をandを用いて連結しています。上限の境界を「<」にするためです。

範囲の指定に「between」も利用可能ですが、境界が「下限≦x≦上限」になってしまいます。

sqlite> select _id,_display_name,date_added from images;
_id         _display_name      date_added
----------  -----------------  ----------
26          1754809710812.jpg  1754809710    ... '25/08/10(Yesterday)
27          1754809710882.jpg  1754809710
28          1754809710888.png  1754809710
29          1754809710892.png  1754809710
30          1754896131558.jpg  1754896131    ... '25/08/11(Today)
31          1754896131565.jpg  1754896131
32          1754896131570.png  1754896131
33          1754896131578.png  1754896131
                                val _dayBefore = calcDayBefore()
                                val _start = _dayBefore.first
                                val _end = _dayBefore.second
								
                                val _projection = null       // 取得するカラム
                                val _selection = "${MediaStore.Images.Media.DATE_ADDED}>=? and ${MediaStore.MediaColumns.DATE_ADDED}<?"
                                val _selectionArgs = arrayOf(
                                    _start.toString(),
                                    _end.toString()
                                )
                                val _sortOrder = null        // ソート順

                                val _cursor = contentResolver.query(
                                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                    _projection,
                                    _selection,
                                    _selectionArgs,
                                    _sortOrder
                                )

                                _cursor?.use { dumpCursor(it) }
補助関数:calcDayBefore()
                                fun calcDayBefore(): Pair<Long, Long> {
                                    val _secOfDay = 24L * 60L * 60L
                                    val _day = System.currentTimeMillis() / (_secOfDay * 1000)
                                    val _start = (_day - 1) * _secOfDay
                                    val _end   = _day * _secOfDay
                                    return Pair(_start, _end)
                                }
Column names = [_id, _data, _size, _display_name, mime_type, title, date_added, date_modified, description, picasa_id, isprivate, latitude, longitude, datetaken, orientation, mini_thumb_magic, bucket_id, bucket_display_name, width, height]
Media id = 26
Media id = 27
Media id = 28
Media id = 29
スポンサーリンク

Appendix:並び替え(sortOrder、queryのみ)

queryにおいて、sortOrderで結果の並び替えが行えます。SQLの「ORDER BY句」と同じです。

sqlite> select _id,_display_name,date_added from images;
_id         _display_name      date_added
----------  -----------------  ----------
26          1754899102809.jpg  1754899102
27          1754553524108.jpg  1754553524
28          1754639949043.jpg  1754639949
29          1754380767668.jpg  1754380767
30          1754899187882.jpg  1754899187
                                val _projection = null       // 取得するカラム
                                val _selection = null        // 検索条件
                                val _selectionArgs = null    // 検索条件の値
                                val _sortOrder = null        // ソート順
                                // val _sortOrder = MediaStore.MediaColumns.DATE_ADDED
                                // val _sortOrder = "${MediaStore.MediaColumns.DATE_ADDED} ASC"
                                // val _sortOrder = "${MediaStore.MediaColumns.DATE_ADDED} DESC"

                                val _cursor = contentResolver.query(
                                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                                    _projection,
                                    _selection,
                                    _selectionArgs,
                                    _sortOrder
                                )

                                _cursor?.use { dumpCursor(it) }

null

プライマリーキー(サンプルは_id)をソート対象にして、昇順に並び替えます。

Column names = [_id, _data, _size, _display_name, mime_type, title, date_added, date_modified, description, picasa_id, isprivate, latitude, longitude, datetaken, orientation, mini_thumb_magic, bucket_id, bucket_display_name, width, height]
Media id = 26
Media id = 27
Media id = 28
Media id = 29
Media id = 30
スポンサーリンク

カラム指定+ASC(昇順、デフォルト)

指定したカラム値をソート対象にして、昇順に並び替えます。

ASCはデフォルトです。「指定なし」はASCになります。

Column names = [_id, _data, _size, _display_name, mime_type, title, date_added, date_modified, description, picasa_id, isprivate, latitude, longitude, datetaken, orientation, mini_thumb_magic, bucket_id, bucket_display_name, width, height]
Media id = 29
Media id = 27
Media id = 28
Media id = 26
Media id = 30

カラム指定+DESC(降順)

指定したカラム値をソート対象にして、降順に並び替えます。

Column names = [_id, _data, _size, _display_name, mime_type, title, date_added, date_modified, description, picasa_id, isprivate, latitude, longitude, datetaken, orientation, mini_thumb_magic, bucket_id, bucket_display_name, width, height]
Media id = 30
Media id = 26
Media id = 28
Media id = 27
Media id = 29
スポンサーリンク

関連記事:

ストレージのリソースは、内部メモリー、外部メモリー、クラウドの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と呼ばれる特別な方法でアクセスします。 他の方法も可能ですが、Media Storeが最も適した方法です。 別の記事で「メディアデータへMedia Storeでアクセス」する方法を、まとめました。 その記事は、基本的なアクセスとして「追加(insert)&読み書き」のみを説明しています。 この記事は、上記に加えて「参照(query)・更新(update)・削除(delete)」を説明します。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
スポンサーリンク