android - 如何使用为 Android 5.0 (Lollipop) 提供的新 SD 卡访问 API?

标签 android android-sdcard android-5.0-lollipop documentfile

背景

Android 4.4 (KitKat),谷歌对 SD 卡的访问非常受限。

从 Android Lollipop (5.0) 开始,开发人员可以使用新的 API,要求用户确认是否允许访问特定文件夹,如 this Google-Groups post 中所述。 .

问题

该帖子将引导您访问两个网站:

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

  • 这看起来像是一个内部示例(可能稍后会在 API 演示中显示),但很难理解发生了什么。
  • http://developer.android.com/reference/android/support/v4/provider/DocumentFile.html

  • 这是新 API 的官方文档,但没有详细说明如何使用它。

    这是它告诉你的:

    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.



    问题

    我对新 API 有几个问题:
  • 你如何真正使用它?
  • 根据帖子,操作系统会记住该应用程序被授予访问文件/文件夹的权限。如何检查是否可以访问文件/文件夹?是否有一个函数可以返回我可以访问的文件/文件夹列表?
  • 你如何在 Kitkat 上处理这个问题?它是支持库的一部分吗?
  • 操作系统上是否有显示哪些应用程序可以访问哪些文件/文件夹的设置屏幕?
  • 如果在同一设备上为多个用户安装应用程序会怎样?
  • 有没有关于这个新 API 的其他文档/教程?
  • 可以撤销权限吗?如果是这样,是否有一个 Intent 被发送到应用程序?
  • 会在选定的文件夹上递归地请求权限吗?
  • 使用权限是否还允许用户有机会根据用户的选择进行多项选择?或者应用程序是否需要明确告知 Intent 允许哪些文件/文件夹?
  • 模拟器上有没有办法尝试新的 API?我的意思是,它有 SD 卡分区,但它作为主要的外部存储,所以对它的所有访问都已经被授予(使用简单的权限)。
  • 当用户用另一张 SD 卡替换 SD 卡时会发生什么?
  • 最佳答案

    很多好问题,让我们深入研究。:)

    你如何使用它?

    这是与 KitKat 中的存储访问框架交互的一个很好的教程:

    https://developer.android.com/guide/topics/providers/document-provider.html#client

    与 Lollipop 中的新 API 交互非常相似。要提示用户选择目录树,您可以启动如下 Intent :

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

    然后在您的 onActivityResult() 中,您可以将用户选择的 Uri 传递给新的 DocumentFile 助手类。这是一个快速示例,它列出了所选目录中的文件,然后创建了一个新文件:
    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 一起使用。例如,您可以使用 Intent.setData() 共享它。与 Intent.FLAG_GRANT_READ_URI_PERMISSION .

    如果您想从 native 代码访问该 Uri,您可以调用 ContentResolver.openFileDescriptor()然后使用 ParcelFileDescriptor.getFd()detachFd()获取传统的 POSIX 文件描述符整数。

    如何检查是否可以访问文件/文件夹?

    默认情况下,通过存储访问框架 Intent 返回的 Uris 不会在重新启动后保留。平台“提供”了持久化权限的能力,但如果你愿意,你仍然需要“获取”权限。在我们上面的例子中,你会调用:
        getContentResolver().takePersistableUriPermission(treeUri,
                Intent.FLAG_GRANT_READ_URI_PERMISSION |
                Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    

    您始终可以通过 ContentResolver.getPersistedUriPermissions() 找出您的应用程序可以访问的持久授权。 API。如果您不再需要访问持久化的 Uri,您可以使用 ContentResolver.releasePersistableUriPermission() 释放它.

    这在 KitKat 上可用吗?

    不,我们不能追溯性地向旧版本的平台添加新功能。

    我可以查看哪些应用程序可以访问文件/文件夹吗?

    当前没有显示此内容的 UI,但您可以在 adb shell dumpsys activity providers 的“授予的 Uri 权限”部分中找到详细信息。输出。

    如果在同一设备上为多个用户安装应用程序会怎样?

    Uri 权限授予在每个用户的基础上是隔离的,就像所有其他多用户平台功能一样。也就是说,在两个不同用户下运行的同一个应用程序没有重叠或共享的 Uri 权限授予。

    可以撤销权限吗?

    支持 DocumentProvider 可以随时撤销权限,例如删除基于云的文档时。发现这些被撤销权限的最常见方法是当它们从 ContentResolver.getPersistedUriPermissions() 中消失时。上文提到的。

    每当为授权中涉及的任一应用程序清除应用程序数据时,权限也会被撤销。

    会在选定的文件夹上递归地请求权限吗?

    是的,ACTION_OPEN_DOCUMENT_TREE Intent 使您可以递归访问现有和新创建的文件和目录。

    这允许多选吗?

    是的,从 KitKat 开始支持多选,您可以通过设置 EXTRA_ALLOW_MULTIPLE 来允许它。开始您的 ACTION_OPEN_DOCUMENT 时 Intent 。您可以使用 Intent.setType()EXTRA_MIME_TYPES缩小可以选择的文件类型:

    http://developer.android.com/reference/android/content/Intent.html#ACTION_OPEN_DOCUMENT

    模拟器上有没有办法尝试新的 API?

    是的,主共享存储设备应该出现在选择器中,即使在模拟器上也是如此。如果您的应用仅使用存储访问框架来访问共享存储,则不再需要 READ/WRITE_EXTERNAL_STORAGE完全没有权限,可以删除它们或使用 android:maxSdkVersion仅在较旧的平台版本上请求它们的功能。

    当用户用另一张 SD 卡替换 SD 卡时会发生什么?

    当涉及到物理媒体时,底层媒体的 UUID(如 FAT 序列号)总是被烧录到返回的 Uri 中。系统使用它来将您连接到用户最初选择的媒体,即使用户在多个插槽之间交换媒体。

    如果用户换了第二张卡,您需要提示获得对新卡的访问权限。由于系统会根据每个 UUID 记住授权,因此如果用户稍后重新插入原始卡,您将继续拥有先前授予的访问权限。

    http://en.wikipedia.org/wiki/Volume_serial_number

    关于android - 如何使用为 Android 5.0 (Lollipop) 提供的新 SD 卡访问 API?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26744842/

    相关文章:

    android - 我想通过我的应用程序在主布局背景中设置保存在 SD 卡上的图像

    Android:从 SD 卡显示图像

    java - android.system.ErrnoException 错误

    javascript - Android 5 上的 Phonegap 滚动问题

    android - 在 android 中将多个图像上传到服务器的最快方法

    java - 使用 'if (position == x)' 替换主 fragment

    Android Studio 不删除我在 Adapter 中的项目?

    android - 如何在不连续向服务器发送请求的情况下在我的 android 应用程序上获取 RSS 提要更新

    java - Android 5,camera2 只使用闪光灯

    android - 禁止下载/安装特定 Android 版本或设备的应用程序