【譯】如何在 Android 5.0 上獲取 SD卡 的訪問(wèn)權(quán)限

因?yàn)樽罱?xiàng)目需要,涉及到 SD卡 的讀寫(xiě)操作,然而申請(qǐng)

<!-- 讀寫(xiě)權(quán)限 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

權(quán)限只能對(duì) SD卡 進(jìn)行讀操作,而沒(méi)有寫(xiě)權(quán)限,也就是說(shuō),Android 在某個(gè)版本中對(duì) SD卡 的讀寫(xiě)權(quán)限進(jìn)行了限制。后在 StackoverFlow 上找到一篇相關(guān)問(wèn)答,解了心中疑惑。在此,對(duì)該問(wèn)答進(jìn)行翻譯并附上相關(guān) Demo,已做備忘。

原文地址:How to use the new SD card access API presented for Android 5.0 (Lollipop)?

問(wèn)

背景

在 Android 4.4(KitKat) 中,Google 對(duì) SD卡 的訪問(wèn)已經(jīng)做了嚴(yán)格的限制。
在 Android 5.0(Lollipop) 中,開(kāi)發(fā)者可以使用 新API 要求用戶(hù)對(duì)某個(gè)指定的文件夾進(jìn)行訪問(wèn)授權(quán),詳見(jiàn):Android 4.4 Samsung Galaxy s4 external sd card is now read only, Remove or option to edit non app files.(譯者注:開(kāi)頭挺搞笑的,都是開(kāi)發(fā)者吐槽 Google 對(duì) SD卡 做了限制)

問(wèn)題

上述文章中有兩個(gè)鏈接:

  1. https://android.googlesource.com/platform/development/+/android-5.0.0_r2/samples/Vault/src/com/example/android/vault/VaultProvider.java#258

    此鏈接中代碼看起來(lái)更像是內(nèi)部示例(可能會(huì)在以后的 API Demo 中出現(xiàn)),但是真的很難理解這部分代碼的意圖。

  2. http://developer.android.com/reference/android/support/v4/provider/DocumentFile.html

    這是 新API 的官方文檔,但是并沒(méi)有多少如何使用的細(xì)節(jié)。(譯者注:這份文檔其實(shí)還是有很多內(nèi)容的,后面會(huì)具體細(xì)講。至于為什么會(huì)有這種差別,可能作者提問(wèn)時(shí),該文檔尚未完善吧~)

    If you really do need full access to an entire subtree of documents, start by launching ACTION_OPEN_DOCUMENT_TREE to let the user pick a directory. Then pass the resulting getData() into fromTreeUri(Context, Uri) to start working with the user selected tree.
    As you navigate the tree of DocumentFile instances, you can always use getUri() to obtain the Uri representing the underlying document for that object, for use with openInputStream(Uri), etc.
    To simplify your code on devices running KITKAT or earlier, you can use fromFile(File) which emulates the behavior of a DocumentsProvider.

對(duì)于新 API 我有以下問(wèn)題:

  1. 新 API 的正確使用方式?
  2. 根據(jù)文檔,系統(tǒng)會(huì)記錄 app 被授予訪問(wèn)權(quán)限的文件和文件夾。那么,我該如何檢測(cè)我對(duì)某個(gè)文件或者文件夾是否有訪問(wèn)權(quán)限?是否有方法獲取可訪問(wèn)的文件或文件夾列表呢?
  3. 在 Android 4.4 上如何處理這個(gè)問(wèn)題?Support Library 是否包含相應(yīng)的解決方案
  4. 系統(tǒng)中是否有對(duì)應(yīng)的界面可以查看哪些 App 可以訪問(wèn)哪些文件。
  5. 在多用戶(hù)的設(shè)備上授權(quán)該如何處理?
  6. 是否有其它關(guān)于新 API 的文檔?
  7. 對(duì) SD卡 的授權(quán)是否可以被取消?如果是,那對(duì)應(yīng)的意圖是什么?
  8. 對(duì)于文件夾授權(quán)是否是遞歸授權(quán)?指代文件夾內(nèi)還嵌套有文件夾。
  9. SD 授權(quán)是否支持多選?或該應(yīng)用程序需要專(zhuān)門(mén)告訴意圖要允許的文件/文件夾嗎?
  10. 模擬器可以測(cè)試新 API 嘛?我的意思是,模擬器具有 SD 卡的分區(qū),但它的作用是主要的外部存儲(chǔ),簡(jiǎn)單使用 android.permission.WRITE_EXTERNAL_STORAGE 是否足夠?
  11. 當(dāng)用戶(hù)替換 SD卡 是會(huì)發(fā)生什么?

來(lái)自 Jeff Sharkey 的回答

這些問(wèn)題問(wèn)的都非常好,讓我們來(lái)深入挖掘下

如何使用新的 API

在 Kitkat 中有一份非常好的關(guān)于與 Storage Access Framework 交互的文檔:Document provider.

新 API 的使用與之很相似。通過(guò)發(fā)送以下 Intent ,讓用戶(hù)在文檔樹(shù)(Directory Tree)中選擇授權(quán)目錄。

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, 42);

onActivityResult() 中,將用戶(hù)選擇的 Uri 傳遞給輔助類(lèi) DocumentFile。以下代碼片段展示了如何列出選中目錄下的文件和如何創(chuàng)建一個(gè)文件。

public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
    if (resultCode == RESULT_OK) {
        Uri treeUri = resultData.getData();
        DocumentFile pickedDir = DocumentFile.fromTreeUri(this, treeUri);

        // List all existing files inside picked directory
        for (DocumentFile file : pickedDir.listFiles()) {
            Log.d(TAG, "Found file " + file.getName() + " with size " + file.length());
        }

        // Create a new file and write into it
        DocumentFile newFile = pickedDir.createFile("text/plain", "My Novel");
        OutputStream out = getContentResolver().openOutputStream(newFile.getUri());
        out.write("A long time ago...".getBytes());
        out.close();
    }
}

DocumentFile.getUri() 返回的 Uri 使用非常靈活,可以與不同的 API 搭配使用。例如,你可以通過(guò) Inetnt.setData() 將 Uri 分享出去,不過(guò)得將 Intent 的 flag 設(shè)置為 Intent.FLAG_GRANT_READ_URI_PERMISSION。

如何檢測(cè)是否對(duì)某個(gè)文件/文件夾有訪問(wèn)權(quán)限

默認(rèn)情況下,通過(guò) Storage Access Framework 獲取的 Uri 授權(quán)并不是永久的,設(shè)備重啟后就會(huì)消失。不過(guò),系統(tǒng)提供了相關(guān)的接口讓授權(quán)永久化,如果需要的話(huà)可自行設(shè)置。在上述代碼,你可以如此設(shè)置:

getContentResolver().takePersistableUriPermission(treeUri,
            Intent.FLAG_GRANT_READ_URI_PERMISSION |
            Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

之后,你就可以通過(guò) ContentResolver.getPersistedUriPermissions() 來(lái)獲取 APP 已經(jīng)被永久授予權(quán)限的 Uri。如果不在需要某個(gè) Uri 的權(quán)限,可以通過(guò) ContentResolver.releasePersistableUriPermission() 來(lái)釋放。

能否在 Kitkat 中使用

不能,因?yàn)樵?API 是在 Lollipop 中添加的

能否知道有哪些 APP 擁有該權(quán)限

能。但是目前是沒(méi)有 UI 界面的,你得通過(guò) adb shell dumpsys activity providers 來(lái)獲取。

在多用戶(hù)的設(shè)備上授權(quán)該如何處理?

與多用戶(hù)系統(tǒng)的其它功能一樣,Uri 授權(quán)也是用戶(hù)獨(dú)立的。因此,同一個(gè) APP 的 Uri 授權(quán)對(duì)每個(gè)用戶(hù)是透明的。

授權(quán)是否可以被取消?

DocumentProvider 支持隨時(shí)撤銷(xiāo)授權(quán)。取消授權(quán)最常見(jiàn)的方法就是通過(guò)上面提到 ContentResolver.releasePersistableUriPermission() 。

當(dāng)清除應(yīng)用的數(shù)據(jù)時(shí),應(yīng)用相關(guān)的授權(quán)也都會(huì)被清除。

對(duì)于文件夾授權(quán)是否是遞歸授權(quán)的?

是的,通過(guò) ACTION_OPEN_DOCUMENT_TREE 的 Intent 獲取到授權(quán)之后,對(duì)該 Uri 下的所有文件都有讀寫(xiě)權(quán)限。

授權(quán)是否支持多選操作?

從 Android 4.4(Kitkat) 起就支持了。您可以在啟動(dòng) ACTION_OPEN_DOCUMENT Intent 時(shí)通過(guò)設(shè)置 EXTRA_ALLOW_MULTIPLE 來(lái)實(shí)現(xiàn)。您可以通過(guò)使用 Intent.setType() 或者 EXTRA_MIME_TYPES 來(lái)設(shè)置可選文件類(lèi)型。具體參考:ACTION_OPEN_DOCUMENT

是否可以在模擬器上嘗試新 API

可以的。如果你的 APP 只使用 Storage Access Framework 訪問(wèn)共享存儲(chǔ),你甚至不再需要 READ/WRITE_EXTERNAL_STORAGE 權(quán)限或者使用 android:maxSdkVersion 在較舊的版本上使用它們。

當(dāng)用戶(hù)替換 SD卡 時(shí)會(huì)發(fā)生什么?

當(dāng)涉及物理介質(zhì)時(shí),底層媒體的 UUID(例如FAT序列號(hào))總是被燒錄到返回的 Uri 中。The system uses this to connect you to the media that the user originally selected, even if the user swaps the media around between multiple slots.(翻譯不了)

如果用戶(hù)替換了新的 SD卡,您需要重新申請(qǐng) SD卡 授權(quán)。 由于系統(tǒng)會(huì)記住基于每個(gè)UUID的授權(quán),如果用戶(hù)以后重新插入,您將繼續(xù)先前授予對(duì)原始卡的訪問(wèn)權(quán)限。

參考:磁盤(pán)序列號(hào)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容