Android 
 
2025.9.29 2023.8.4 Coskx Lab  
0 「DocumentFile」を使う場合のgradleに関する変更 20250930
「DocumentFile」を使う場合には,
1 はじめに
Android11(R)API30以降ではファイル保存領域の扱いが厳格になっています。
内部ストレージ内のアプリ固有のディレクトリアプリ固有のディレクトリのページへ  
外部ストレージ内のアプリ固有のディレクトリアプリ固有のディレクトリのページへ  
共有ストレージ内のメディア用途のディレクトリ 
共有ストレージ内の一般用途のディレクトリ本ページで解説しています。  
 
補足 外部ストレージ内のアプリ固有のディレクトリを見られるファイルマネージャアプリはないかもしれません。
ストレージ詳細  (表示/非表示) 
Android デベロッパーのAndroid 11(API レベル 30)の説明では,内部ストレージ・外部ストレージは論理的な区別で,アプリの視点からは,自分しかアクセスできない領域が内部ストレージ,そうでない領域が外部ストレージになります。
(https://developer.android.com/about/versions/11/privacy/storage)
(内部・外部ストレージというのは,物理的な内部・外部という意味ではないので本体内ストレージとSDカードを区別しているわけではありません。Android 11の前にはこのような区別の表現も使われていたため,注意が必要です。)
共有ストレージは外部ストレージ内にあります。(SDカードも外部ストレージに含まれます。)
〇内部ストレージは,AndroidDevice(スマホなど)のファイルマネージャアプリで見ることはできません。
〇外部ストレージ内の共有領域はファイルマネージャアプリで見ることが出来ます。
〇外部ストレージ内のアプリ固有のディレクトリは見ることがファイルマネージャアプリで出来ません。(権限のあるファイルマネージャアプリでは見ることができるかもしれません。USB接続のPCからは見ることができます。
Android StudioのDevice Explorerを使うと,内部ストレージ内のアプリ固有のディレクトリ,外部ストレージ内のアプリ固有のディレクトリもすべて見ることができます。
使いかた
WindowsPCとAndroid DeviceをUSBケーブルで接続する。 
Android Studioで上部メニューから View > Tool Windows > Device Explorer で起動する。 
 
見え方(Android DeviceでのOS実装によるので,多少の違いはあると思います。)
アプリ固有の内部ストレージ: data/data/[applicationId]/files/ に見える 
アプリ固有の外部ストレージ: storage/self/primary/android/data/[applicationId]/files/Documents/ に見える。 
 
    Android Deviceのディレクトリ(フォルダ)構造
      SDカードが実装されていないDeviceの例
storage/self/primaryのところはstorage/emulated/0と表示されることもあります。
その他
AndroidDeviceのファイルマネージャアプリではstorage/self/primary/(storage/emulated/0)が見えている 
storage/self/primary/(storage/emulated/0)には,Android,DCIM,Download,Movies,Music,Picturesなどがある。 
WindowsPCのExplorerでAndroid DeviceをUSBメモリとして見ているときは,storage/self/primary/(storage/emulated/0)が見える。アプリ固有の外部メモリ領域も見える。 
 
2 使用環境
Windows 11 64-bit 
Android Studio Narwhal | 2025.1.1 Patch 1 
java (JavaVersion.VERSION_11) 
Build Target 35 (24以降で動作検証済) 
 
3 共有ストレージの操作
Android11(R)API30より前までのファイル操作を行うアプリでは,インストール時において,ユーザに全ストレージへの権限を与えてもらうようになっていました。
ファイル指示方式  ファイルの書き込みを行うときは,そのたびにどのディレクトリにどのようなファイル名で書き込むのかをユーザに指示してもらいます。→ 4 ファイル指示方式 ディレクトリ指示方式  ファイル書き込み・読み込みで,1つのファルダ(外部ストレージ)を常に使うような場合には,予め使用するディレクトリをユーザに指示してもらいます。(自アプリ専用ディレクトリを作成する場合に対応します。DownloadやDCIMなどのディレクトリの並びに,専用ディレクトリを作ることができます。作成したディレクトリはファイルマネージャアプリや他アプリからも読み込めます。) → 5 ディレクトリ指示方式  
なお,本説明内容のテストプログラムをダウンロードできます。ダウンロード 「4.ファイル指示方式共有ストレージ操作」 ダウンロード 「5.ディレクトリ指示方式共有ストレージ操作」 
4 ファイル指示方式 (共有ストレージの操作)
ファイルの書き込み・読み込みを行うときは,ストレージを使うので必ずユーザに指示してもらいます。
4.1 テキストファイルの書き込み
ファイル書き込み用のIntentのインスタンスをインスタンス変数として宣言をしておきます。
private Intent intent_createTextfile;
onCreate()中でそのIntentのインスタンス変数を初期化し,またActivityResultLauncherのインスタンス変数も初期化し,戻ってきた時の作業(ファイルのUriを使ったファイル書き込み)も前もって記述しておきます。
private void setIntentCreateTextfile() {
ファイル書き込みを行うまでに書き込みたいtextをStringインスタンス変数に確定しておきます。
//書込用textはインスタンス変数でなければならないが,書込Intent呼び出しまでに確定されていればよい
そうすると,画面にピッカーが現れるので,ユーザが書き込みのためのディレクトリとファイル名を設定します。
4.2 テキストファイルの読み込み
ファイル読み込み用のIntentのインスタンスをインスタンス変数として宣言をしておきます。
private Intent intent_loadTextfile;
onCreate()中でそのIntentのインスタンス変数を初期化し,またActivityResultLauncherのインスタンス変数も初期化し,戻ってきた時の作業(ファイルのUriを使ったファイル読み込み)も前もって記述しておきます。
private void setIntentLoadTextfile() {
Launcherでファイル読み込み用のIntentを起動します。
launcher_loadTextfile.launch(intent_loadTextfile);
そうすると,画面にピッカーが現れるので,ユーザが読み込みのためのファイルを指示します。
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
で読み込みます。(これはsetIntentLoadTextfile()で,'result -> {'の中に記述されている)
4.3 画像ファイルの書き込み
ファイル書き込み用のIntentのインスタンスをインスタンス変数として宣言をしておきます。
private Intent intent_createImagefile;
onCreate()中でそのIntentのインスタンス変数を初期化し,またActivityResultLauncherのインスタンス変数も初期化し,戻ってきた時の作業(ファイルのUriを使ったファイル書き込み)も前もって記述しておきます。
private void setIntentCreateImagefile() {
ファイル書き込みを行うまでに書き込みたい画像をBitmapインスタンス変数に確定しておきます。
//書込用ファイル名(候補)は書込Intent呼び出しまでに確定されていればよい
そうすると,画面にピッカーが現れるので,ユーザが書き込みのためのディレクトリとファイル名を設定します。
4.4 画像ファイルの読み込み
ファイル読み込み用のIntentのインスタンスをインスタンス変数として宣言をしておきます。
private Intent intent_loadImagefile;
onCreate()中でそのIntentのインスタンス変数を初期化し,またActivityResultLauncherのインスタンス変数も初期化し,戻ってきた時の作業(ファイルのUriを使ったファイル読み込み)も前もって記述しておきます。
private void setIntentLoadImagefile() {
Launcherでファイル読み込み用のIntentを起動します。
launcher_loadImagefile.launch(intent_loadImagefile);
そうすると,画面にピッカーが現れるので,ユーザが読み込みのためのファイルを指示します。
Bitmap bitmap = BitmapFactory.decodeStream(bufferedInputStream);
で読み込みます。(これはsetIntentLoadImagefile()で,'result -> {'の中に記述されている)
4.5 実行の様子
保存・読み出しのできるディレクトリは「ダウンロード」およびその支配下のディレクトリのみのようです。
5 ディレクトリ指示方式 (共有ストレージの操作)
特定のディレクトリに対して,多くの操作を行うようなアプリでは,特定のディレクトリをユーザに1回だけ指示してもらい,後はアプリ側がファイル名を決めて作業します。
5.1 ユーザ指定の作業ディレクトリの取得
ディレクトリ指示用のIntentのインスタンスをインスタンス変数として宣言をしておきます。のインスタンスをインスタンス変数として宣言をしておきます。 
private Intent intent_getFolder;
onCreate()中でそのIntentのインスタンス変数を初期化し,またActivityResultLauncherのインスタンス変数も初期化し,ピッカーから戻ってきた時の作業も前もって記述しておきます。
intent_getFolder = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
必要な項目を指定して,void assignOwnDirectory()でLauncherでファイル読み込み用のIntentを起動します。
private void assignOwnDirectory() {
なお,次回アプリ起動時にはOnresumeでgetPersistedUriPermissions()を使って保存されたディレクトリのUriを読み出し,Uri ownDirUriとDocumentFile ownDirectoryを生成しています。
@Override
5.2 ディレクトリ内でのファイル操作
ユーザが指定したディレクトリのUriが得られたら,ファイルの操作が出来ます。
5.2.1 ファイル書き込み
書き込みたい文字列を
//ファイル保存 ただし,既存ファイルがあれば上書きする
5.2.2 ファイル読み込み
対象とするファイルのUriが分かっていれば,次のメソッドでテキストファイルを読み込むことができます。
private String readFile(Uri uri) {
5.2.3 ディレクトリ内のファイル一覧
次のメソッドでディレクトリ内のファイル一覧が得られます。
private void showOwnFolder() {
5.2.4 ディレクトリ内のファイル全消去
次のメソッドでディレクトリ内のファイル全消去になります。
private void deleteAllFiles() {
5.3 実行の様子
このアプリでは,ユーザ指定の作業ディレクトリの取得は一回のみできます。
6 補足
SAF(ストレージアクセスフレームワーク)を利用したとき,共有ストレージ操作について権限付与は不要です。(AndroidManifest.xmlへの権限記載,権限取得操作はいらない) 
これまで,API28以前におけるファイル操作では,書き込みや消去の直後に,MediaScannerServiceに登録しないとOSへの反映が出来ませんでしたが,それはすべて不要になりました。 
ピッカーで扱える範囲はOSのバージョンやAndroid Deviceの設計に依存しているため,Android Deviceにより見え方が異なります。 
ピッカーでGoogle Driveも扱えるとなっていますが,Google Driveアプリがインストールされている必要があるとされています。(未確認) 
ピッカーでGoogle Driveも扱えるとなっていますが,ファイル指示方式のときのみで,ディレクトリ指示方式では使えません。(ファイル指示方式のとき,ピッカー起動後の左上の3本線設定からGoogle Driveが選べます。) 
 
7 まとめ
共有ストレージにおいて,ファイル指示方式によってファイル操作について示しました。
共有ストレージにおいて,ディレクトリ指示方式によってファイル操作について示しました。
ファイル書き込み(上書き,追記) 
ファイル読み込み 
ディレクトリ内ファイル一覧 
ディレクトリ内全ファイル削除 
 
ファイル指示方式・ディレクトリ指示方式のどちらでもファイルのUriが得られてしまえば,操作は全く同じになります。