「外部ストレージ_セカンダリ」にアクセスする方法について、まとめます。
「外部ストレージ_セカンダリ」は、デバイス間(または、アプリ間)で共有するデータを保存するストレージです。
デバイスとは、他の携帯端末やパソコンなどを指します。
ストレージがSDカードのような取り外し可能なリソース上に構築されているので、データの共有は取り外したストレージを他のデバイスへ取り付けて行います。
「外部ストレージ_セカンダリ」に保存したデータは、他のデバイスに公開することになるので、秘匿性の高いデータの保存に適しません。
※環境:Android Studio Narwhal | 2025.1.1 Patch 1
外部ストレージ_セカンダリ
「外部ストレージ_セカンダリ」はデバイス間(または、アプリ間)で共有するデータを保存するストレージです。
保存されるデータは、「ドキュメント・他のファイル」、「アプリ別」に分類されます。
# pwd /storage/0000-0000 # ls -ld * Android/data/com.example.myapp_a/* drwxrwx--x 3 root sdcard_rw 2048 2025-07-31 12:18 Android drwxrwx--x 2 u0_a85 sdcard_rw 2048 2025-07-31 12:20 Android/data/com.example.myapp_a/files drwxrwx--x 2 root sdcard_rw 2048 2025-07-31 12:18 LOST.DIR ※エミュレータ(API28)で確認
# pwd /storage/0E15-260A # ls -ld * Android/data/com.example.myapp_a/* drwxrwx--x 2 root sdcard_rw 2048 2025-07-31 12:35 Alarms drwxrwx--x 3 root sdcard_rw 2048 2025-07-31 12:35 Android drwxrwx--x 2 u0_a146 sdcard_rw 2048 2025-07-31 12:35 Android/data/com.example.myapp_a/files drwxrwx--x 2 root sdcard_rw 2048 2025-07-31 12:35 DCIM drwxrwx--x 2 root sdcard_rw 2048 2025-07-31 12:35 Download drwxrwx--x 2 root sdcard_rw 2048 2025-07-31 16:35 LOST.DIR drwxrwx--x 2 root sdcard_rw 2048 2025-07-31 12:35 Movies drwxrwx--x 2 root sdcard_rw 2048 2025-07-31 12:35 Music drwxrwx--x 2 root sdcard_rw 2048 2025-07-31 12:35 Notifications drwxrwx--x 2 root sdcard_rw 2048 2025-07-31 12:35 Pictures drwxrwx--x 2 root sdcard_rw 2048 2025-07-31 12:35 Podcasts drwxrwx--x 2 root sdcard_rw 2048 2025-07-31 12:35 Ringtones ※エミュレータ(API29)で確認
# pwd /storage/12EB-0D1D # ls -ld * Android/data/com.example.myapp_a/* drwxrwx--- 2 root media_rw 2048 2025-07-31 12:41 Alarms drwxrwx--- 3 root media_rw 2048 2025-07-31 12:41 Android drwxrwx--- 2 root media_rw 2048 2025-07-31 12:41 Android/data/com.example.myapp_a/files drwxrwx--- 2 root media_rw 2048 2025-07-31 12:41 Audiobooks drwxrwx--- 2 root media_rw 2048 2025-07-31 12:41 DCIM drwxrwx--- 2 root media_rw 2048 2025-07-31 12:41 Documents drwxrwx--- 2 root media_rw 2048 2025-07-31 12:41 Download drwxrwx--- 2 root media_rw 2048 2025-07-31 12:41 LOST.DIR drwxrwx--- 3 root media_rw 2048 2025-07-31 12:41 Movies drwxrwx--- 3 root media_rw 2048 2025-07-31 12:41 Music drwxrwx--- 2 root media_rw 2048 2025-07-31 12:41 Notifications drwxrwx--- 3 root media_rw 2048 2025-07-31 12:41 Pictures drwxrwx--- 2 root media_rw 2048 2025-07-31 12:41 Podcasts drwxrwx--- 2 root media_rw 2048 2025-07-31 12:41 Ringtones ※エミュレータ(API30)で確認
データを「他のデバイスと共有する」は「他のデバイスに公開する」と同意です。悪意のある人物がSDカードを手に入れた場合に、自分のパソコンを使って内容を盗み見ることが出来てしまします。
ですので、秘匿性の高いデータの保存に適しません。
アクセスの方法
「Java/Android API」によるアクセスが可能です。
ただし、問題点が2つあります。それを理解した上で利用してください。
問題点1ですので、「アクセス可能な範囲はアプリ別の領域のみ」と考えるのが良さそうです。
無理にアクセスしたければ、ドキュメント・他のファイルのパスを、アプリ別の相対パスで作り出すしかありません(△)。
ですので、「デバイス間で(または、アプリ間)で共有するデータを保存する」という、外部ストレージ_セカンダリの本来の目的が達成できません。
外部ストレージ_セカンダリのアクセスについては「SAF」の利用が最適です(◎)。SAFであれば、共有データを作成できます。
外部ストレージ セカンダリ /storage/NNN-NNN/ | Java / Android API | Media Store API | SAF | |
---|---|---|---|---|
ドキュメント 他のファイル | Downloads | 他:× | 他:◎wr |
|
Documents | ||||
Android (Android/dataを除く) |
||||
ユーザーフォルダ | ||||
ユーザーファイル | ||||
アプリ別 (Android/data/パッケージ名/files) | 他:△ rp | 他:○wr |
||
※自:自アプリのファイル 他:他アプリのファイル ◎:アクセスできる(最適) ○:アクセスできる △:アクセスできる(不適または制限有) ×:アクセスできない 空:アクセスできない(サポート外) w:書き込みできる r:読み出しできる p:パーミッションが必要 |
外部ストレージ セカンダリ /storage/NNN-NNN/ | Java / Android API | Media Store API | SAF | |
---|---|---|---|---|
ドキュメント 他のファイル | Downloads | 他:× | 他:◎wr |
|
Documents | ||||
Android (Android/dataを除く) |
||||
ユーザーフォルダ | ||||
ユーザーファイル | ||||
アプリ別 (Android/data/パッケージ名/files) | 他:× | 他:○wr |
||
※自:自アプリのファイル 他:他アプリのファイル ◎:アクセスできる(最適) ○:アクセスできる △:アクセスできる(不適または制限有) ×:アクセスできない 空:アクセスできない(サポート外) w:書き込みできる r:読み出しできる p:パーミッションが必要 |
外部ストレージ セカンダリ /storage/NNN-NNN/ | Java / Android API | Media Store API | SAF | |
---|---|---|---|---|
ドキュメント 他のファイル | Downloads | 他:× | 他:◎wr |
|
Documents | ||||
Android (Android/dataを除く) | 他:× |
|||
ユーザーフォルダ | ||||
ユーザーファイル | ||||
アプリ別 (Android/data/パッケージ名/files) | 他:× | 他:× |
||
※自:自アプリのファイル 他:他アプリのファイル ◎:アクセスできる(最適) ○:アクセスできる △:アクセスできる(不適または制限有) ×:アクセスできない 空:アクセスできない(サポート外) w:書き込みできる r:読み出しできる p:パーミッションが必要 |
「Java/Android API」によるアクセスは「間接的なアクセス」で行います。間接的なアクセスとは、以下の通りです。
Fileによる間接的なアクセスFileオブジェクトへ前もって保存場所を指定しておきます。Fileオブジェクトが示す場所(パス)にアクセスする方法です。
パーミッションの取得
一部のアクセス(表で「p」の付くもの)で、パミッションの取得が必要です。
※詳細は「外部ストレージへアクセスするパーミッションと権限の範囲」を参照
アプリ別
間接的なアクセスでデータを外部ストレージへ書き込みます。
パス(保存先)の取得
getExternalFilesDirsを使うと、Android APIに定義されているボリューム毎のパスが、配列で取得できます。
※ボリュームについては「ストレージの用途別記憶領域とボリューム」を参照
val _bucket = Environment.DIRECTORY_PICTURES //val _backet = null val _ctxExtDirs: Array<File> = this@Activity.getExternalFilesDirs(_bucket)
Environment.DIRECTORY_PICTURES ⇒ /storage/emulated/0/Android/data/パッケージ名/files/Pictures /storage/NNN-NNN/Android/data/パッケージ名/files/Pictures null ⇒ /storage/emulated/0/Android/data/パッケージ名/files /storage/NNN-NNN/Android/data/パッケージ名/files
このパスの配列から、プログラムで外部ストレージ_セカンダリのパスを抽出します。
注意点は「ストレージのマウントを確認する」ことです。SDカードが取り外されている場合もあるからです。
fun Context.getExternalSecondaryFilesDir( type: String?, index: Int = 0 ): File? { val _dirs = ArrayList<File>() val _manager = getSystemService(StorageManager::class.java) val _files = getExternalFilesDirs(type) _files.forEach { val _volume = _manager.getStorageVolume(it) val _isActive = _volume?.let { (! it.isPrimary) && (it.state == "mounted") }?:false if(_isActive) _dirs.add(it) } if(_dirs.size > index) return _dirs[index] else throw IOException("Directory not found !") }
val _bucket = Environment.DIRECTORY_PICTURES // val _bucket = Environment.DIRECTORY_DCIM // val _bucket = Environment.DIRECTORY_MOVIES // val _bucket = Environment.DIRECTORY_MUSIC // val _bucket = Environment.DIRECTORY_ALARMS // val _bucket = Environment.DIRECTORY_NOTIFICATIONS // val _bucket = Environment.DIRECTORY_PODCASTS // val _bucket = Environment.DIRECTORY_RINGTONES // val _bucket = Environment.DIRECTORY_DOCUMENTS // val _bucket = Environment.DIRECTORY_DOWNLOADS // val _bucket = Environment.DIRECTORY_DOWNLOADS // val _bucket = Environment.DIRECTORY_DOCUMENTS // val _bucket = "UserDir" val _ctxExtDir: File? = this@Activity.getExternalSecondaryFilesDir(_bucket)
パスを取得すると同時に、パスの示すディレクトリツリー全体(Android以下)が、実際のストレージ上に自動作成されます。
また、ストレージが利用できない場合はnullになることがあります。
書き込み
File#outputStreamを用いると、Fileに指定した保存先へ書き込まれます。
fun writeFile_Basic(data: ByteArray, file: File) { try { file.outputStream().use { stream -> stream.write(data) } } catch (e: Exception) { Log.i(TAG, "[write] ${e}") } }
getExternalSecondaryFilesDirは「null」を返すことがあります。回避するためにパスのnull判定を行い、nullでない場合のみ書き込みを行うようにします。
val _path = this@Activity.getExternalSecondaryFilesDir(bucket) // 末端までのディレクトリを自動作成 _path?.let { val _file = File(it, filename) writeFile_Basic(_data, _file) }
# pwd /storage/0000-0000/Android/data/パッケージ名/files # ls -ld Pictures Pictures/* drwxrwx--x 2 u0_a85 sdcard_rw 2048 2025-08-01 01:08 Pictures -rwxrwx--x 1 u0_a85 sdcard_rw 30 2025-08-01 01:08 Pictures/sample.txt ※エミュレータ(API28)で実行 ※「bucket = Environment.DIRECTORY_PICTURES」の時
書き込み以外の操作
その他の操作(読み出し、ファイル削除、ディレクトリ作成/削除、ファイル一覧)は、内部ストレージにアクセスする場合と同じです。
※詳細は「内部ストレージにJava/Android APIでアクセス」を参照
関連記事: