Android — Android 11的檔案讀寫問題

JLin
8 min readApr 8, 2021

--

https://developer.android.com/training/data-storage

如果你APP會讀寫內部空間或者是記憶卡上的資料,對於Android 11在儲存空間中可能會有一些問題

目前在App中存取檔案資料的方始有這幾種

App-specific storage

也就是可以在in-app中使用data/data/{packageName}/files/ 去存取資料,但這個缺點就是app資料就不見了

Media

可分享的多媒體資料存取,也就是你如果想把一些auidio/video等等存入或讀取在手機中,就可以透過這個 MediaStore API來取用

Documents and other files

可以讀取其它類型的分享型內容或是下載的資料等等,透過storage access framework來讀取,這種方式就是透過系統檔案總管讓使用者允許你要存取的資料位置,你就可以讀取這的資料

App preference

就是shared Preference 或是data store的方式

Database

Android 10 之前

而在Android 10 之前你可以透過

READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE

去對整個空間獲取存取權限

Android 10

然後再Android 10的時候,如果你的APP在 android 10想要讀寫記憶卡位置,你可以透過上面兩個權限,然後加上Manifest 定義

https://developer.android.com/training/data-storage/use-cases#opt-out-scoped-storage

android:requestLegacyExternalStorage="true"

就可以正常的跟10之前一樣取得權限,或是你也可以使用

preserverLegacyExternalStorage 的定義

https://developer.android.com/reference/android/R.attr#preserveLegacyExternalStorage

這個定義的目的是,如果是更新升級,他一樣可以獲得原本的使用權限,但如果當你的這個app是新安裝的話,就無法取得Write權限

這時候就必須使用scoped storage 去存取資料,這個之後會談到

Android 11的時候

於是在Android 11的時候,android:requestLegacyExternalStorage將會被忽略,等於你只能拿到 read file的permission,另外WRITE_EXTERNAL_STORAG也是一樣,官方文件這樣寫著,也就是你宣告了他也不會理你

https://developer.android.com/training/data-storage#permissions

if your app targets Android 11 (API level 30) or higher, the WRITE_EXTERNAL_STORAGE permission doesn't have any effect on your app's access to storage.

那怎麼辦

11卻增加了一個拿取全部空間的宣告權限

MANAGE_EXTERNAL_STORAGE

可以拿到整個儲存空間read/write的權限,不過使用這個權限的APP必須是合理的使用才能上架到google

如果你的APP沒有在合理使用的狀況下,譬如應該是一個檔案總管軟體,掃毒軟體,或是檔案轉移軟體等等,上架到Google必須經過審核才能通過,否則你就不能使用該權限,或是你沒有要上架到Google,就可以使用在Menifest宣告

MANAGE_EXTERNAL_STORAGE

然後執行request

ActivityCompat.requestPermissions(
this, arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.MANAGE_EXTERNAL_STORAGE
),
1
)

然後透過判斷去開啟使用者同意開啟你的權限

if (!Environment.isExternalStorageManager()) {
val intent = Intent(ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION)
intent.data = Uri.fromParts("package", packageName, null)
startActivity(intent)
}

譬如這個畫面,同意之後你就可以跟以前一樣開心的使用讀取權限了

正常使用下建議

建議使用scoped storage 去讓使用者同意與取得

但Android 11對於使用有一些新限制

  1. 不能存取Root權限
  2. 不能存取/android/data/跟 /android/obb/的路徑

https://developer.android.com/training/data-storage/shared/documents-files#document-tree-access-restrictions

--

--

JLin

台中 / JAVA / Android /Kotlin / Kotlin Native 對於Kotlin衍生的JVM等技術 Compose for web / desktop / Ktor Server,生成式AI (Gemini/OpenAI)各式應用, 都有小興趣