SAF(StorageAccessFramework)とは?
- Androidでストレージ周りを取り扱う仕組みのこと。
- filepathではなく、Uriを使いコンテンツを特定する。
- Contentスキーマなので、Uri.fromFile()による相互変換は効かない。
- 特定のアプリは2020のアップデートで全てのSDKバージョンで対応必須になる(後述)
これは利用できない.
// uri <- file
val uri = Uri.fromFile(file)
// file <- uri
val file = File(uri.path)
filepathでよくない?
- アプリ内領域を使う分にはfilepathでよい。
- getExternalFilesDir()はそのまま利用可能。
- アプリ内領域なので、アプリストレージを消去とかしたら当然消える
// 使える
Context.getExternalFilesDir()
// 使えない(API29よりDeprecated)
Environment.getExternalStorageDirectory()
Environment.getExternalStoragePublicDirectory()
じゃあどういうときに使うの
- [対象範囲別ストレージ]
(https://developer.android.com/preview/privacy/scoped-storage) - AndroidQより外部ストレージのアクセスが更に厳格化された
- 2020のアップデートでは全てのSDKバージョンで対応必須にするとのこと
- 外部アプリが作成したものにアクセスする、外部ストレージに書き込みを行う等
- SAFを介して権限をユーザに要求することでアクセスが許可されるようになる
何を使えばいいかの判断
判断材料 | 使うもの |
---|---|
アプリ内のみで使います | Content.getExternalFilesDir() |
アプリ外でメディアを永続保存・共有します | MediaStore + ContentResolver |
アプリ外でファイルを永続保存・共有します | SAF + ContentResolver |
SAFの基本
- Intentを投げてピッカーを開く
- ユーザが操作する
- onActivityResultでUriを受け取って処理
fun performFileSearch() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "image/*"
}
startActivityForResult(intent, READ_REQUEST_CODE)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
resultData?.data?.also { uri ->
Log.i(TAG, "Uri: $uri")
showImage(uri)
}
}
}
SAFで権限付きアクセス
- 権限を要求するIntentを発行
- ユーザがいいよって言う
- Resultでuriを受け取り、ContentResolverでクエリを発行
SDカード保存(Android9以前)
- CreateAccessIntentの廃止により、権限を覚えさせておくことができなくなった?
- リフレクションでパスを強引に取得したりURIいじくり回して権限を覚えさせるとか試されているらしい -> リンク
MediaStore
対象別範囲ストレージで外部に存在するが、それがメディア(画像・音楽・音声)である場合はMediaStoreからUriを取得できる
- 写真 - 保存場所: MediaStore.Images
- 動画 - 保存場所: MediaStore.Video
- 音楽ファイル - 保存場所: MediaStore.Audio
AndroidDeveloperに書いてあることが昨日と違う・・・最新情報を確認してください。