Edited at

【Android】対象別範囲ストレージとSAF


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()



じゃあどういうときに使うの


  • 対象範囲別ストレージ

  • 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で権限付きアクセス


  1. 権限を要求するIntentを発行

  2. ユーザがいいよって言う

  3. Resultでuriを受け取り、ContentResolverでクエリを発行

SDカード保存(Android9以前)


  • CreateAccessIntentの廃止により、権限を覚えさせておくことができなくなった?


    • リフレクションでパスを強引に取得したりURIいじくり回して権限を覚えさせるとか試されているらしい -> リンク








MediaStore

対象別範囲ストレージで外部に存在するが、それがメディア(画像・音楽・音声)である場合はMediaStoreからUriを取得できる


  • 写真 - 保存場所: MediaStore.Images

  • 動画 - 保存場所: MediaStore.Video

  • 音楽ファイル - 保存場所: MediaStore.Audio

AndroidDeveloperに書いてあることが昨日と違う・・・最新情報を確認してください。