Android10以降のファイル保存について
情報が錯綜していて修正が難しかったです。
でもようやく修正方法が分かりました。
- 好き勝手に保存ができなくなったこと
- Android10以降は対応が必要なこと
- ACTION_CREATE_DOCUMENTを使えばいいこと
これはアプリ開発にとって非常に大切なので、
Android10以降の正しいファイル保存について解説します。
このページの目次
Android11からストレージ周りの大幅アプデがあった
Android11からストレージの扱いがかなり変わります。
セキュリティ的にすごく厳格になる感じです。
▼ 公式ではこう解説されている
Android 11(API レベル 30)では、プラットフォームをさらに強化し、外部ストレージ上のアプリとユーザーのデータの保護を改善しています。このリリースでは、メディアの raw ファイルパスへのオプトイン アクセス、メディアの一括編集操作、ストレージ アクセス フレームワークの UI の更新などの機能強化が行われています。
引用元 : https://developer.android.com/about/versions/11/privacy/storage?hl=ja
実際にはこういうアップデートが行われてます。
- 対象範囲別ストレージの適用
- アプリ固有のディレクトリが作成できない
- メディアファイルへのアクセス
問題なのは「対象範囲別ストレージ」という所
これは中々ググっても理解するのが難しく、情報が出てこなくて困りました。でも雰囲気的には今までのように気軽にファイルが作成できないことは分かります。
そして詳しく調べてみた結果・・・「対象範囲別ストレージ」について以下の事実が判明しました。
- Android10以降は今までのメソッドが使えない
具体的には Environment.getExternalStorageDirectory() とか Environment.getExternalStoragePublicDirectory() はパスを返すだけになり、ファイルの読み書きが一切できない
- ACTION_CREATE_DOCUMENTがファイル保存に使える
これはAPIレベル19から追加されたアクションだが、これを使うことでAndroid10・Android11以降でもファイル保存が行える(後述)
ざっとまとめると変更点はこういう感じです。
Android10では「対象範囲別ストレージ」対応を先延ばし可能
これはAndroid10限定なのですが・・・
Android11から必須になる「対象範囲別ストレージ」への対応を一時的に先延ばしすることもできます。具体的にはマニフェストファイル(AndroidManifest.xml)を以下のように変更するだけです。
▼ AndroidManifest.xml
1 2 3 4 5 6 |
<manifest ... > <!-- applicationタグへの変更. --> <application android:requestLegacyExternalStorage="true" ... > <!-- 省略... --> </application> </manifest> |
このようにapplicationタグにおいて android:requestLegacyExternalStorage="true" のように設定するだけ。これで今までのコードが一応動きます。
今までのコードというのは Environment.getExternalStoragePublicDirectory() などを使ってファイル・画像などを保存してる場合ですね。
でも根本的な対処にはなりません。
正しいのはACTION_CREATE_DOCUMENTによるファイル保存
最終的にたどり着いた答えがこれ
ACTION_CREATE_DOCUMENTによるファイル保存
これならAndroid10以降には問題なく対応可能です。
具体的な手順をのせておくと次の通り
1.パーミッション許可ダイアログの表示
これは次記事で解説しました。
▼ Androidでパーミッション許可ダイアログの出し方
APIレベル23以上だったら対応必須
この記事では android.permission.WRITE_EXTERNAL_STORAGE に対するパーミッション許可の取り方をコード例にしてます。
もし読み込みだけが必要なら android.permission.READ_EXTERNAL_STORAGE だけをパーミッション許可するだけでOKです。そこは読み替えてください。
2.ACTION_CREATE_DOCUMENTで保存UIを起動
そしたら保存するためのファイルピッカーを起動します。
具体的には次コードを書けばOKです。
▼ 保存UI起動のコード例
1 2 3 4 5 6 7 8 9 |
/// 保存ファイル名 String fileName = "hogehoge.png"; /// ファイル保存のためのピッカーUI起動 Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); intent.putExtra(Intent.EXTRA_TITLE, fileName); startActivityForResult(intent, 12345); |
▼ 以下のようなファイルピッカーが起動する
このようにファイル保存のためのピッカーUIが起動します。まるでWindowsにおける「名前を付けて保存」のような処理が可能です。
3.作成されたファイルにデータを書き込む
保存UIでファイルが選択されたあと・・・
その作成済みファイルに内容を書き込んでいきます。
▼ このようなコードを書いてみた
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
@Override public void onActivityResult(int requestCode, int resultCode, Intent resultData) { super.onActivityResult(requestCode, resultCode, resultData); if (requestCode == 12345 && resultCode == RESULT_OK) { if(resultData.getData() != null) { // データを作成されたファイルに書き込む Uri uri = resultData.getData(); byte[] bytes = /* 画像データとか... */; try(OutputStream outputStream = getContentResolver().openOutputStream(uri)) { if(outputStream != null){ /// データを書き込み outputStream.write(bytes); /// トーストで保存された旨を通知 Toast.makeText(this, "保存場所 : " + uri.getPath(), Toast.LENGTH_LONG).show(); } } catch(Exception e){ e.printStackTrace(); } } } } |
これで無事ファイル保存に成功。
Android10以降で「ファイル保存がうまくいかない!」というリスクを回避できるし、UI的にも分かりやすくなったと思います。
Android10以降のファイル保存の要点だけまとめ
大事なところだけ箇条書きするとこの通り
- 今までのように好き勝手保存できない
Android11からのアプデにより、これまで使ってきた Environment.getExternalStorageDirectory() などがパスを返すだけになり、ファイルの読み書きが一切できない。
- 対象範囲別ストレージへの対応が必要
具体的にはACTION_CREATE_DOCUMENTによるファイル保存をすればAndroid10以降でも問題なく対応可能。ただしAndroid10では一時的だけど対応の先延ばしも可能
以上、Android10以降でのファイル保存の正しい方法でした。
ではまた、バイバイ(@^^)/~~~