メディアデータはアプリ間で共有されるので、アプリがデータへアクセスするには、アクセス許可が必要です。
この許可の権限の範囲は、プライバシー保護の観点から、徐々に狭められてきました。
そして、対象範囲別ストレージにおいて、他アプリが所有するファイルへの書き込みは、URI(ファイル)毎の許可が必要になっています。
許可の取得は、ユーザと対話をする形式(ダイアログ)で行われます。手順が少し複雑です。
ここに「他アプリが所有するファイルへ書き込み」する方法を、まとめます。
※環境:Android Studio Narwhal Feature Drop | 2025.1.2 Patch 1
アクセス許可
対象範囲別ストレージにおいて、他のアプリが所有するファイルへアクセスする場合の許可は、図のようになっています。

読み出し
通常のパーミッションシステムで、メディアデータへアクセスする許可を取得できます。
※詳細は「外部ストレージへアクセスするパーミッションと権限の範囲」を参照
許可を得たアプリは、ファイルを読み出し可能です。
書き込み
後述する方法で、URI(ファイル)へアクセスする許可を取得できます。
許可を得たアプリは、ファイルへ書き込み可能です。
注意
「URIに出されるアクセス許可」と「パーミッションシステムの出すアクセス許可」は独立しています。両者の間に関係性は無く、全くの別物です。
許可の取得手順
アクセス許可の取得は、次のような手順で行います。

手順1
URIのアクセス許可をチェック。許可が無い場合はClientアプリを起動します。
手順2
Clientアプリはダイアログを表示して、ユーザにアクセス許可・拒否の判定を求めます。
手順3
ダイアログによる判定の結果をコールバックで返します。
手順1~3はアプリケーションコンポーネント(Activity)の連携と同じ動作です。
※詳細は「App component:Activity」を参照
書き込みの例
書き込みの具体的な例です。※Activity Result APIで記述しています。
コールバックの定義と登録
registerForActivityResultはコールバックの定義と登録を行います。
ラムダ式がコールバックです。「許可・拒否」はresultCodeで判断できます。
そして、このコールバックを利用するコンポーネントのランチャー(_MediaWriteDialog : ActivityResultLauncher)を返します。
private lateinit var targetUri: Uri
private lateinit var targetData: ByteArray
...
val _MediaWriteDialog = registerForActivityResult(
ActivityResultContracts.StartIntentSenderForResult()
) { result ->
if(result.resultCode == RESULT_OK) { // アクセス許可
writeToUri_Resolver(targetData, targetUri)
}
else { } // アクセス拒否
}
registerForActivityResultは、LifecycleOwnerのcurrentStateが「STARTED」より前のタイミングで実行して下さい。Activityのライフサイクルと密接に関係しています。
※LifecycleOwnerについては「ライフサイクル対応コンポーネント作成」を参照
許可チェックとダイアログ起動
URIのアクセス許可をチェックします。許可の無い場合はClientアプリを起動(launch)し、ダイアログを表示させます。
launchの引数は取得したい許可のリクエスト(MediaWriteReq : IntentSenderRequest)です。書き込みを行いたいのでMediaStore.createWriteRequestを使って、リクエストを構築しています。
@RequiresApi(Build.VERSION_CODES.R)
fun onWrite(uri: Uri, id: Long, data: ByteArray) {
try {
val _uri = ContentUris.withAppendedId(uri, id)
val _chkUriPerm = checkUriPermission( // 許可チェック
_uri, Process.myPid(), Process.myUid(),
FLAG_GRANT_WRITE_URI_PERMISSION
)
if (_chkUriPerm == PackageManager.PERMISSION_GRANTED) {
writeToUri_Resolver(data, _uri)
} else {
targetUri = _uri
targetData = data // ↓↓ダイアログを起動
_MediaWriteDialog.launch(MediaWriteReq(contentResolver, _uri))
}
} catch (e: Exception) { Log.i(TAG, "[onWrite] ${e}") }
}
@RequiresApi(Build.VERSION_CODES.R)
fun MediaWriteReq(resolver: ContentResolver, uri: Uri): IntentSenderRequest {
val _pendingIntent = MediaStore.createWriteRequest(resolver, listOf(uri))
val _intentSenderRequest = IntentSenderRequest.Builder(
_pendingIntent.intentSender
).build()
return _intentSenderRequest
}
MediaStore.createWriteRequestはAPI≧30から利用が可能です。
書き込みの実行
書き込みの実行(onWrite)を行うと、ダイアログが表示されて、ユーザに「許可・拒否」の判定を求めます。
val _uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val _id = 1000000033
// val _uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI
// val _id = 1000000034
// val _uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
// val _id = 1000000035
onWrite(_uri, _id, _data)
メディアテーブルの状態
# pwd
/data/data/com.google.android.providers.media.module/databases
# sqlite3 ./external.db
sqlite> .headers on
sqlite> .mode column
sqlite> select _id,_display_name,_data from images;
_id _display_name _data
---------- ------------- -------------------------------------
1000000033 leaf.jpg /storage/emulated/0/Pictures/leaf.jpg
sqlite> select _id,_display_name,_data from video;
_id _display_name _data
---------- ------------- --------------------------------------
1000000034 myroom.mpeg /storage/emulated/0/Movies/myroom.mpeg
sqlite> select _id,_display_name,_data from audio;
_id _display_name _data
---------- ------------- --------------------------------------
1000000035 akatonbo.mp3 /storage/emulated/0/Music/akatonbo.mp3
※エミュレータ(API 33)で確認
メディアデータの状態
# pwd
/storage/emulated/0
# ls -l Pictures/* Movies/* Music/* */.thumbnails/*
-rw-rw---- 1 u0_a173 media_rw 5349 2025-08-19 13:23 Movies/.thumbnails/1000000034.jpg
-rwxrwx--- 1 u0_a173 media_rw 195759 2025-08-19 13:23 Movies/myroom.mpeg
-rw-rw---- 1 u0_a173 media_rw 11472 2025-08-19 13:28 Music/.thumbnails/1000000035.jpg
-rwxrwx--- 1 u0_a173 media_rw 45366 2025-08-19 13:27 Music/akatonbo.mp3
-rw-rw---- 1 u0_a173 media_rw 34088 2025-08-19 13:23 Pictures/.thumbnails/1000000033.jpg
-rwxrwx--- 1 u0_a173 media_rw 47891 2025-08-19 13:22 Pictures/leaf.jpg
※エミュレータ(API 33)で確認
なお、ダイアログに表示される画像はサムネイル(.thumbnail/メディア情報のID.jpg)です。
「許可」となったURIは、このアプリによる書き込みが可能です。
例えば上書きするのであれば、通常のアクセスと同様に、下記の拡張関数で行えます。
※メディアファイルの書き込みについては「ImagesデータへMedia Storeでアクセス(API≧29)」を参照
fun Context.writeToUri_Resolver(data: ByteArray, uri: Uri) {
try {
contentResolver.openOutputStream(uri, "w")?.use { outputStream ->
outputStream.write(data)
outputStream.flush()
}
} catch (e: Exception) { Log.i(TAG, "[writeToUri] ${e}") }
}
「既存のファイルへ書き込み」と考えるのが妥当
対象範囲別ストレージにおいて、既存のファイルへ書き込み(更新)を行う場合、アクセス許可の必要性は、次のようになります。
自アプリのファイル ⇒ 許可が不要
他アプリのファイル ⇒ 許可が必要
ファイルの所有者により、必要性の有無が変わります。
ファイルの所有者を判別し、必要な場合のみに許可の取得を行いたいところです。しかし、API内に所有者を判別する手段が準備されていません。
ですので、所有者に関係なく、全ての既存ファイルにおいて、許可の取得を行うしかありません。
既存のファイルへ書き込むと、古いデータは消失します。
不本意な書き込み(間違った書き込み、悪意のあるアプリによる改ざん)は大問題なので、書き込みを行う前に、所有者に関係なく確認を行う行為は理に適ています。

そう考えると、アクセス許可を取得する場面は、「他アプリが所有するファイルへ書き込み」よりも「既存ファイルへ書き込み」する時と考える方が、妥当かも知れません。
Appendix:その他のリクエスト
createWriteRequest以外のリクエストも準備されています。
リクエスト
(MediaStore.***) | 概要 | 許可後の動作 |
createWriteRequest | 書き込み許可を要求 | URIへ書き込み(更新)可能 |
createDeleteRequest | 削除する許可を要求 | 即時に削除 |
createTrashRequest | 廃棄する許可を要求 | ゴミ箱へ廃棄したと見なされる
(is_trashedカラムを設定)
一定時間経過後にシステムが削除 |
createFavoriteRequest | お気に入り登録する許可を要求 | お気に入り登録したと見なされる
(is_favoriteカラムを設定) |
※許可はアプリに対して与えられる |
アプリが既存ファイル(他アプリが所有するファイル)へ「〇〇する」許可を取得するためのリクエストです。
関連記事:
ストレージのリソースは、内部メモリー、外部メモリー、クラウドの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 ...
「外部ストレージ_プライマリ」に保存されるメディアデータは、データベースで管理されます。 Media Storeを使うと、このデータベースを通してメディアデータへアクセスできます。 データベース(SQLite)が持つ検索機能を使ってアクセス対象を絞り込めるので、とても便利です。 この記事は、「絞り込みアクセス」について、まとめます。 ※環境:Android Studio Narwhal | 2025.1.1 Patch 1 ...
メディアデータはMedia Storeを使ったアクセスが最適です。 その理由は、メディア特有の付加情報を使って、データを管理できるからです。 管理されたデータはブログラムから扱い易いです。また、ユーザの使い勝手(エクスペリエンス)も向上します。 バケットはメディアデータを管理する付加情報の一つです。 無くてもアクセスは可能ですが、積極的に取り入れた方が良いと思います。 今回は「ファイル名とバケットを指定したアクセス」について、まとめます。 「API≦28」と「API≧29」で動作が異なります。注意して下さい。 ※環境:Android Studio Narwhal Feature Drop | 2025.1.2 ...
メディアデータはMedia Storeを使ったアクセスが最適です。 その理由は、メディア特有の付加情報を使って、データを管理できるからです。 管理されたデータはブログラムから扱い易いです。また、ユーザの使い勝手(エクスペリエンス)も向上します。 バケットはメディアデータを管理する付加情報の一つです。 無くてもアクセスは可能ですが、積極的に取り入れた方が良いと思います。 今回は「ファイル名とバケットを指定したアクセス」について、まとめます。 「API≦28」と「API≧29」で動作が異なります。注意して下さい。 ※環境:Android Studio Narwhal Feature Drop | 2025.1.2 ...