本文介绍了如何使用为Android 5.0(Lollipop)提供的新SD卡访问API?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Android 4.4 (KitKat)上,限制访问SD卡.

On Android 4.4 (KitKat), Google has made access to the SD card quite restricted.

从Android Lollipop(5.0)开始,开发人员可以使用新的API,要求用户确认是否允许访问特定文件夹,如 此Google网上论坛帖子 .

As of Android Lollipop (5.0), developers can use a new API that asks the user to confirm to allow access to specific folders, as written on the this Google-Groups post .

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

The post directs you to visit two websites:

这看起来像一个内部示例(可能稍后在API演示中显示),但是很难理解发生了什么.

This looks like an inner example (perhaps to be shown on the API demos later), but it's quite hard to understand what's going on.

这是新API的正式文档,但没有提供足够的使用方法详细信息.

This is the official documentation of the new API, but it doesn't tell enough details about how to use it.

这是告诉你的内容:

在浏览DocumentFile实例树时,您始终可以使用 getUri()获取表示基础文档的Uri 该对象,用于openInputStream(Uri)等.

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.

要简化运行KITKAT或更早版本的设备上的代码,您可以 使用fromFile(File)来模拟DocumentsProvider的行为.

To simplify your code on devices running KITKAT or earlier, you can use fromFile(File) which emulates the behavior of a DocumentsProvider.

问题

我对新API有一些疑问:

The questions

I have a few questions about the new API:

  1. 您如何真正使用它?
  2. 根据该帖子,操作系统将记住该应用已获得访问文件/文件夹的权限.您如何检查是否可以访问文件/文件夹?是否有一个函数可以向我返回我可以访问的文件/文件夹列表?
  3. 您如何在Kitkat上处理此问题?它是支持库的一部分吗?
  4. 操作系统上是否有一个设置屏幕,可显示哪些应用有权访问哪些文件/文件夹?
  5. 如果在同一设备上为多个用户安装了一个应用程序,会发生什么情况?
  6. 是否有关于此新API的其他文档/教程?
  7. 可以撤销权限吗?如果是这样,是否有意图发送给应用程序?
  8. 是否会在选定文件夹上递归请求许可权?
  9. 使用许可是否还可以使用户有机会根据用户的选择进行多项选择?还是应用程序需要明确告知意图允许哪些文件/文件夹?
  10. 模拟器上是否有办法尝试新的API?我的意思是,它具有SD卡分区,但是它可以用作主要的外部存储,因此已经授予了对其的所有访问权限(使用简单权限).
  11. 当用户用另一张SD卡替换SD卡时会发生什么?
  1. How do you really use it?
  2. According to the post, the OS will remember that the app was given a permission to access the files/folders. How do you check if you can access the files/folders? Is there a function that returns me the list of files/folders that I can access?
  3. How do you handle this problem on Kitkat? Is it a part of the support library?
  4. Is there a settings screen on the OS that shows which apps have access to which files/folders?
  5. What happens if an app is installed for multiple users on the same device?
  6. Is there any other documentation/tutorial about this new API?
  7. Can the permissions be revoked? If so, is there an intent that's being sent to the app?
  8. Would asking for the permission work recursively on a selected folder?
  9. Would using the permission also allow to give the user a chance of multiple selection by user's choice? Or does the app need to specifically tell the intent which files/folders to allow?
  10. Is there a way on the emulator to try the new API ? I mean, it has SD-card partition, but it works as the primary external storage, so all access to it is already given (using a simple permission).
  11. What happens when the user replaces the SD card with another one?

推荐答案

很多好的问题,让我们深入探讨.:)

Lots of good questions, let's dig in. :)

这是与KitKat中的存储访问框架进行交互的出色教程:

Here's a great tutorial for interacting with the Storage Access Framework in KitKat:

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

与Lollipop中的新API交互非常相似.要提示用户选择目录树,您可以启动这样的意图:

Interacting with the new APIs in Lollipop is very similar. To prompt the user to pick a directory tree, you can launch an intent like this:

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

然后在onActivityResult()中,可以将用户选择的Uri传递给新的DocumentFile帮助器类.这是一个简短的示例,列出了所选目录中的文件,然后创建了一个新文件:

Then in your onActivityResult(), you can pass the user-picked Uri to the new DocumentFile helper class. Here's a quick example that lists the files in the picked directory, and then creates a new file:

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共享它.

The Uri returned by DocumentFile.getUri() is flexible enough to use with may different platform APIs. For example, you could share it using Intent.setData() with Intent.FLAG_GRANT_READ_URI_PERMISSION.

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

If you want to access that Uri from native code, you can call ContentResolver.openFileDescriptor() and then use ParcelFileDescriptor.getFd() or detachFd() to obtain a traditional POSIX file descriptor integer.

默认情况下,通过Storage Access Framework意图返回的Uris在重新启动后不会保留.该平台提供"了保留权限的能力,但是如果需要,您仍然需要获取"该权限.在上面的示例中,您将调用:

By default, the Uris returned through Storage Access Frameworks intents are not persisted across reboots. The platform "offers" the ability to persist the permission, but you still need to "take" the permission if you want it. In our example above, you'd call:

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

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

You can always figure out what persisted grants your app has access to through the ContentResolver.getPersistedUriPermissions() API. If you no longer need access to a persisted Uri, you can release it with ContentResolver.releasePersistableUriPermission().

否,我们无法向旧版本的平台追溯添加新功能.

No, we can't retroactively add new functionality to older versions of the platform.

当前没有显示此内容的用户界面,但是您可以在adb shell dumpsys activity providers输出的授予的Uri权限"部分中找到详细信息.

There's currently no UI that shows this, but you can find the details in the "Granted Uri Permissions" section of adb shell dumpsys activity providers output.

Uri权限授予是按用户隔离的.也就是说,在两个不同用户下运行的同一应用程序没有重叠或共享的Uri权限授予.

Uri permission grants are isolated on a per-user basis, just like all other multi-user platform functionality. That is, the same app running under two different users has no overlaping or shared Uri permission grants.

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

The backing DocumentProvider can revoke permission at any time, such as when a cloud-based document is deleted. The most common way to discover these revoked permissions is when they disappear from ContentResolver.getPersistedUriPermissions() mentioned above.

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

Permissions are also revoked whenever app data is cleared for either app involved in the grant.

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

Yep, the ACTION_OPEN_DOCUMENT_TREE intent gives you recursive access to both existing and newly created files and directories.

是的,自KitKat开始支持多种选择,您可以通过在启动ACTION_OPEN_DOCUMENT意图时设置EXTRA_ALLOW_MULTIPLE来允许选择.您可以使用Intent.setType()EXTRA_MIME_TYPES缩小可以选择的文件类型:

Yep, multiple selection has been supported since KitKat, and you can allow it by setting EXTRA_ALLOW_MULTIPLE when starting your ACTION_OPEN_DOCUMENT intent. You can use Intent.setType() or EXTRA_MIME_TYPES to narrow the types of files that can be picked:

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

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

Yep, the primary shared storage device should appear in the picker, even on the emulator. If your app only uses the Storage Access Framework for accessing shared storage, you no longer need the READ/WRITE_EXTERNAL_STORAGE permissions at all and can remove them or use the android:maxSdkVersion feature to only request them on older platform versions.

涉及物理介质时,基础介质的UUID(例如FAT序列号)始终会刻录到返回的Uri中.即使用户在多个插槽之间交换媒体,系统也会使用它将您连接到用户最初选择的媒体.

When physical media is involved, the UUID (such as FAT serial number) of the underlying media is always burned into the returned 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.

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

If the user swaps in a second card, you'll need to prompt to gain access to the new card. Since the system remembers grants on a per-UUID basis, you'll continue to have previously-granted access to the original card if the user reinserts it later.

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

这篇关于如何使用为Android 5.0(Lollipop)提供的新SD卡访问API?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-03 12:26