使用作用域目录访问

应用(如照片应用)通常只需要访问外部存储中的特定目录,例如 Pictures 目录。现有的外部存储访问方法未经专门设计,无法轻松地为这些类型的应用提供定向目录访问。例如:

  • 在您的清单中请求 READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE 将允许访问外部存储上的所有公共目录,这可能导致访问的内容超出应用需要的内容。
  • 使用存储访问框架通常会让您的用户通过一个系统 UI 选取目录,如果应用始终访问同一个外部目录,则该操作没有任何必要。

Android 7.0 提供简化的 API 来访问常见的外部存储目录。

访问外部存储目录

使用 StorageManager 类获取适当的 StorageVolume 实例。然后,通过调用该实例的 StorageVolume.createAccessIntent() 方法创建一个 intent。使用此 intent 访问外部存储目录。要获取所有可用卷的列表,包括可移动介质卷,请使用 StorageManager.getStorageVolumes()

如果您有关于特定文件的信息,请使用 StorageManager.getStorageVolume(File) 获取包含该文件的 StorageVolume。调用此 StorageVolume 上的 createAccessIntent() 以访问文件的外部存储目录。

在次要卷(例如外部 SD 卡)上,调用 createAccessIntent() 以请求访问整个卷而不是特定目录时将传入“null”。如果您向主要卷传入“null”,或者如果您传入无效的目录名,createAccessIntent() 将返回“null”。

以下代码段展示了如何在主要共享存储中打开 Pictures 目录:

StorageManager sm = (StorageManager)getSystemService(Context.STORAGE_SERVICE);
StorageVolume volume = sm.getPrimaryStorageVolume();
Intent intent = volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
startActivityForResult(intent, request_code);

系统尝试授予对外部目录的访问权限,并使用一个简化的 UI 向用户确认访问权限(如果需要):

图 1. 一个请求访问 Pictures 目录的应用。

如果用户授予访问权限,系统会调用 onActivityResult() 替换方法(结果代码为 RESULT_OK),以及包含 URI 的 intent 数据。使用提供的 URI 访问目录信息,与使用存储访问框架返回的 URI 类似。

如果用户不授予访问权限,系统将调用onActivityResult() 替换方法(结果代码为 RESULT_CANCELED),以及空 intent 数据。

获得特定外部目录的访问权限也会获得该目录中子目录的访问权限。

访问可移动介质上的目录

要使用作用域目录访问来访问可移动介质上的目录,首先请添加一个用于侦听 MEDIA_MOUNTED 通知的 BroadcastReceiver,例如:

<receiver
    android:name=".MediaMountedReceiver"
    android:enabled="true"
    android:exported="true" >
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_MOUNTED" />
        <data android:scheme="file" />
    </intent-filter>
</receiver>

当用户装载可移动介质(如 SD 卡)时,系统将发送一则 MEDIA_MOUNTED 通知。此通知会在 intent 数据中提供一个 StorageVolume 对象,您可以使用此对象访问可移动介质上的目录。以下示例可以访问可移动介质上的 Pictures 目录:

// BroadcastReceiver has already cached the MEDIA_MOUNTED
// notification Intent in mediaMountedIntent
StorageVolume volume = (StorageVolume)
    mediaMountedIntent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME);
volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
startActivityForResult(intent, request_code);

最佳做法

如果可能,请保留外部目录访问 URI,这样就不必重复要求用户授予访问权限。在用户授予访问权限后,调用 getContentResolver(),在返回 ContentResolver 后,使用目录访问 URI 调用 takePersistableUriPermission()。系统将保留此 URI,后续的访问请求将返回 RESULT_OK,且不会向用户显示确认 UI。

如果用户拒绝授予外部目录访问权限,请勿立即再次请求访问权限。反复不停地请求访问权限会导致糟糕的用户体验。如果用户拒绝了一项请求,而应用再次请求访问权限,UI 会显示一个 Don't ask again 复选框:

图 1. 应用第二次请求访问可移动介质。

如果用户选择 Don't ask again 并拒绝请求,您的应用向给定目录提出的所有未来请求都将被自动拒绝,并且将不会有请求 UI 呈现给用户。