Android R如何訪問文件,修改文件,你們對R適配了嗎
心理分析:新Android特性一直都是面試官重點中的重點接下來,會問你他的原理 你是怎麼看。 它的優缺點。爲什麼比其他的好。從原理層來解析。這纔是最難的。這篇文章 從原理層說明他們的區別
更多面試內容,面試專題,flutter視頻 全套,音視頻從0到高手開發。
關注GitHub:https://github.com/xiangjiana/Android-MS
免費獲取面試PDF合集
Android R 越來越近了,最近 Google 又發佈了 Android R Beta 的第五個版本,眼瞅着這進度,在今年 R3 季度,Android R 就正式和用戶見面了,在此之前,開發者必然又是面臨的一波讓人頭疼的適配。
瞭解新特性,首推應該去看官方文檔,官方已經給出了一份完整的新特性文檔,在發佈的這段時間,也一直在保持同步的更新。而作爲開發者,我們更關心的是如何解決在我們現有的 App 上,保證 Android R 的兼容性問題。
今天就給推薦給大家一份適配文檔,以開發者的角度列一份適配清單,在 Android R 還沒來之前,先了解需要做什麼,以及怎麼做,到時候纔不至於措手不及。
這份文檔的出自 OPPO 開放平臺,可能有人會覺得是 KPI 工程,但是你想想這些廠商每年耗巨資研發的旗艦機,用着最新的硬件,當然要搭配最新的系統,而用戶在旗艦機上的體驗,也是他們最關心的,所以每次 Android 發佈新系統,這些廠商也在推進自己應用市場上 App 的適配工作。
你只需要想想他們做這件事的動機,就能知道這份文檔肯定是花了心思的。文檔我看過一遍,從場景出發來分析原因,並附上解決方案,很有參考意義。
文檔比較長,大家可以先收藏,再跳躍閱讀看自己關注的點
一. 背景說明
本文檔是基於谷歌安卓R 的 beta1 版本的變更輸出的兼容性整改指導,如果後續 beta 版本有新的變更和新的特性,我們也會刷新文檔的相關章節內容,請開發者持續關注。
二. 存儲空間限制
2.1 背景
爲了讓用戶更好地控制自己的文件,並限制文件混亂的情況,Android R 修改了 APP 訪問外部存儲中文件的方法。外部存儲的新特性被稱爲 Scoped Storage。
Android R 仍然使用 READ_EXTERNAL_STORAGE 和 WRITE_EXTERNAL_STORAGE 作爲面向用戶的存儲相關運行時權限,但現在即使獲取了這些權限,訪問外部存儲也受到了限制。APP 需要這些運行時權限的情景發生了變化,且各種情況下外部存儲對 APP 的可見性也發生了變化。
在 Scoped Storage 新特性中,外部存儲空間被分爲兩部分:
● 公共目錄 :Downloads、Documents、Pictures 、DCIM、Movies、Music、Ringtones 等
- 公共目錄下的文件在 APP 卸載後,不會刪除。
- APP 可以通過 SAF(System Access Framework)、MediaStore 接口訪問其中的文件。
● App-specific 目錄
- APP 卸載後,數據會清除。
-
APP 的私密目錄,APP 訪問自己的 App-specific 目錄時無需任何權限
Android R 規定了 APP 有兩種外部存儲空間視圖模式:Legacy View、Filtered View。
● Filtered View
- App 可以直接訪問 App-specific 目錄,但不能直接訪問 App-specific 外的文件。訪問公共目錄或其他 APP 的 App-specific 目錄,只能通過 MediaStore、SAF、或者其他 APP 提供的 ContentProvider、FileProvider 等訪問。
● Legacy View
- 兼容模式。與 Android R 以前一樣,申請權限後 App 可訪問外部存儲,擁有完整的訪問權限。
在 Android R 上,target SDK 大於或等於 29 的 APP 默認被賦予 Filtered View,反之則默認被賦予 Legacy View。APP 可以在 AndroidManifest.xml
中設置新屬性 requestLegacyExternalStorage
來修改外部存儲空間視圖模式,true 爲 Legacy View,false 爲 Filtered View。可以使用 Environment.isExternalStorageLegacy()
這個 API 來檢查 APP 的運行模式。APP 開啓 Filtered View 後,Scoped Storage 新特性對 APP 生效。
Android R 除了劃分外部存儲和定義 Filtered View,還在查詢、讀寫文件的一些細節上做了改進或限制,例如圖片文件中的地理位置信息將不再默認提供、查詢 MediaProvider 獲得的 DATA 字段不再可靠、新增了文件的 Pending 狀態等等。這些細節的具體內容請參考適配方案章節。
2.2 兼容性影響
Scoped Storage 對於 APP 訪問外部存儲方式、APP 數據存放以及 APP 間數據共享,都產生很大影響。請開發者注意以下的兼容性影響事項。
2.2.1 無法新建文件
問題原因: 直接使用自身 App-specific 目錄以外的路徑新建文件。
問題分析: 在 Android R 上,APP 只允許在自身 App-specific 目錄以內通過路徑生成的文件。
解決方案: APP 自身 App-specific 目錄下新建文件的方法與文件路徑,請參見 2.3.1;如果要在公共目錄下新建文件,使用 MediaStore 接口,請參見 2.3.2;如果要在任意目錄下新建文件,需要使用 SAF,請參見 2.3.3。
2.2.2 無法訪問存儲設備上的文件
問題原因 1: 直接使用路徑訪問公共目錄文件。
問題分析 1: 在 Android R 上,APP 默認只能訪問外部存儲設備上的 App-specific 目錄。
解決方法 1: 參見 2.3.2 和 2.3.3,使用 MediaStore 接口訪問公共目錄中的多媒體文件,或者使用 SAF 訪問公共目錄中的任意文件。注意:從 MediaStore 接口中查詢到的 DATA 字段將在 Android R 開始廢棄,不應該利用它來訪問文件或者判斷文件是否存在;從 MediaStore 接口或者 SAF 獲取到文件 Uri 後,請利用 Uri 打開 FD 或者輸入輸出流,而不要轉換成文件路徑去訪問。
問題原因 2: 使用 MediaStore 接口訪問非多媒體文件。
問題分析 2: 在 Android R 上,使用 MediaStore 接口只能訪問公共目錄中的多媒體文件。
解決方法 2: 使用 SAF 向用戶申請文件或目錄的讀寫權限,請參見 2.3.3。
2.2.3 無法正確分享文件
問題原因: APP 將 App-specific 目錄中的私有文件分享給其他 APP 時,使用了 file://
類型的 Uri。
問題分析: 在 Android R 上,由於 App-specific 目錄中的文件是私有受保護的,其他 APP 無法通過文件路徑訪問。
解決方案: 參見 2.3.4,使用 FileProvider,將 content://
類型的 Uri 分享給其他 APP。
2.2.4 無法修改存儲設備上的文件
問題原因 1: 直接使用路徑訪問公共目錄文件。
問題分析 1: 同 2.2.2。
解決方案 1: 同 2.2.2,請使用正確的公共目錄文件訪問方式。
問題原因 2: 使用 MediaStore 接口獲取公共目錄多媒體文件的 Uri 後,直接使用該 Uri 打開 OutputStream 或文件描述符。
問題分析 2: 在 Android R 上,修改公共目錄文件,需要用戶授權。
解決方案 2: 從 MediaStore 接口獲取公共目錄多媒體文件 Uri 後,打開 OutputStream 或 FD 時,注意 catch RecoverableSecurityException,然後向用戶申請該多媒體文件的刪改權限,請參見 2.3.2.6;使用 SAF 獲取到文件或目錄的 Uri 時,用戶已經授權讀寫,可以直接使用,但要注意 Uri 權限的時效,請參見 2.3.3.6。
2.2.5 應用卸載後文件意外刪除
問題原因: 將想要保留的文件保存在外部存儲的 App-specific 目錄下。
問題分析:在 Android R 上,卸載 APP 默認刪除 App-specific 目錄下的數據。
解決方案: APP 應該將想要保留的文件通過 MediaStore 接口保存到公共目錄下,請參見 2.3.2。默認情況下,MediaStore 接口會將非媒體類文件保存到 Downloads 目錄下,推薦 APP 指定一級目錄爲 Documents。如果 APP 想要在卸載時保留 App-specific 目錄下的數據,要在 AndroidManifest.xml 中聲明 android:hasFragileUserData="true",這樣在 APP 卸載時就會有彈出框提示用戶是否保留應用數據。
2.2.6 無法訪問圖片文件中的地理位置數據
問題原因: 直接從圖片文件輸入流中解析地理位置數據。
問題分析: 由於圖片的地理位置信息涉及用戶隱私,Android R 上默認不向 APP 提供該數據。
解決方案: 申請 ACCESS_MEDIA_LOCATION 權限,並使用 MediaStore.setRequireOriginal() 接口更新文件 Uri,請參見 2.3.5.1 。
2.2.7 Fota 升級問題
問題原因: Fota 升級後,APP 被卸載,重新安裝後無法訪問到 APP 數據。
問題分析: Scoped Storage 新特性只對 Android R 上新安裝的 APP 生效。設備從 Android R 之前的版本升級到 Android R,已安裝的 APP 獲得 Legacy View 視圖。這些 APP 如果直接通過路徑的方式將文件保存到了外部存儲上,例如外部存儲的根目錄,那麼 APP 被卸載後重新安裝,新的 APP 獲得 Filtered View 視圖,無法直接通過路徑訪問到舊數據,導致數據丟失。
解決方案: APP 應該修改保存文件的方式,不再使用路徑的方式直接保存,而是採用 MediaStore 接口將文件保存到對應的公共目錄下。在 Fota 升級前,可以將 APP 的用戶歷史數據通過 MediaStore 接口遷移到公共目錄下。此外,APP 應當改變訪問 App-specific 目錄以外的文件的方式,請使用 MediaStore 接口或者 SAF。
2.3 適配指導
Android R Scoped Storage 新特性谷歌官方適配文檔:
https://developer.android.google.cn/preview/privacy/scoped-storage
OPPO 適配指導如下,分爲:訪問 APP 自身 App-specific 目錄文件、使用 MediaStore 訪問公共目錄、使用 SAF 訪問指定文件和目錄、分享 App-specific 目錄下文件和其他細節適配。
2.3.1 訪問 APP 自身 App-specific 目錄文件
無需任何權限,APP 即可直接使用文件路徑來讀寫自身 App-specific 目錄下的文件。獲取 App-specific 目錄路徑的接口如下表所示。
如下,以新建並寫入文件爲例。
final File[] dirs = getExternalFilesDirs("Documents");
File primaryDir = null;
if (dirs != null && dirs.length > 0) {
primaryDir = dirs[0];
}
if (primaryDir == null) {
return;
}
File newFile = new File(primaryDir.getAbsolutePath(), "MyTestDocument");
OutputStream fileOS = null;
try {
fileOS = new FileOutputStream(newFile);
if (fileOS != null) {
fileOS.write("file is created".getBytes(StandardCharsets.UTF_8));
fileOS.flush();
}
} catch (IOException e) {
LogUtil.log("create file fail");
} finally {
try {
if (fileOS != null) {
fileOS.close();
}
} catch (IOException e1) {
LogUtil.log("close stream fail");
}
}
2.3.2 使用 MediaStore 訪問公共目錄
APP 無法直接訪問公共目錄下的文件。MediaStore 爲 APP 提供了訪問公共目錄下媒體文件的接口。APP 在有適當權限時,可以通過 MediaStore 查詢到公共目錄文件的 Uri,然後通過 Uri 讀寫文件。
MediaStore 相關的 Google 官方文檔:
https://developer.android.google.cn/reference/android/provider/MediaStore
2.3.3 APP 通過 MediaStore 訪問文件所需要的權限
通過 MediaStore 提供的 Uri,使用 ContentResolver 的 insert 接口,將文件保存到公共目錄下。不同的 Uri,可以保存到不同的公共目錄中
更多面試內容,面試專題,flutter視頻 全套,音視頻從0到高手開發。
關注GitHub:https://github.com/xiangjiana/Android-MS
免費獲取面試PDF合集