使用外部存储
每个兼容 Android 的设备都支持可用于保存文件的共享“外部存储”。 该存储可能是可移除的存储介质(例如 SD 卡)或内部(不可移除)存储。 保存到外部存储的文件是全局可读取文件,而且,在计算机上启用 USB 大容量存储以传输文件后,可由用户修改这些文件。
注意:如果用户在计算机上装载了外部存储或移除了介质,则外部存储可能变为不可用状态,并且在您保存到外部存储的文件上没有实施任何安全性。 所有应用都能读取和写入放置在外部存储上的文件,并且用户可以移除这些文件。
使用作用域目录访问
在 Android 7.0 或更高版本中,如果您需要访问外部存储上的特定目录,请使用作用域目录访问。 作用域目录访问可简化您的应用访问标准外部存储目录(例如Pictures
目录)的方式,并提供简单的权限 UI,清楚地详细介绍应用正在请求访问的目录。 有关作用域目录访问的更多详情,请参阅
使用作用域目录访问
。
获取外部存储的访问权限
要读取或写入外部存储上的文件,您的应用必须获取 READ_EXTERNAL_STORAGE
或 WRITE_EXTERNAL_STORAGE
系统权限。 例如:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
如果您同时需要读取和写入文件,则只需请求 WRITE_EXTERNAL_STORAGE
权限,因为此权限也隐含了读取权限要求。
注:从 Android 4.4 开始,如果您仅仅读取或写入应用的私有文件,则不需要这些权限。 如需了解更多信息,请参阅下面有关保存应用私有文件的部分。
检查介质可用性
在使用外部存储执行任何工作之前,应始终调用 getExternalStorageState()
以检查介质是否可用。介质可能已装载到计算机,处于缺失、只读或其他某种状态。 例如,以下是可用于检查可用性的几种方法:
/* Checks if external storage is available for read and write */
public boolean isExternalStorageWritable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
return true;
}
return false;
}
/* Checks if external storage is available to at least read */
public boolean isExternalStorageReadable() {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state) ||
Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
return true;
}
return false;
}
getExternalStorageState()
方法将返回您可能需要检查的其他状态(例如介质是否处于共享 [连接到计算]、完全缺失、错误移除等状态)。 当您的应用需要访问介质时,您可以使用这些状态向用户通知更多信息。
保存可与其他应用共享的文件
在媒体扫描程序中隐藏您的文件
在您的外部文件目录中包含名为 .nomedia
的空文件(注意文件名中的点前缀)。 这将阻止媒体扫描程序读取您的媒体文件,并通过 MediaStore
内容提供程序将其提供给其他应用。 但如果您的文件真正是应用的私有文件,则应该将其保存在应用私有的目录中。
一般而言,应该将用户可通过您的应用获取的新文件保存到设备上的“公共”位置,以便其他应用能够在其中访问这些文件,并且用户也能轻松地从该设备复制这些文件。 执行此操作时,应使用共享的公共目录之一,例如 Music/
、Pictures/
和 Ringtones/
等。
要获取表示相应的公共目录的 File
,请调用 getExternalStoragePublicDirectory()
,向其传递您需要的目录类型,例如 DIRECTORY_MUSIC
、DIRECTORY_PICTURES
、 DIRECTORY_RINGTONES
或其他类型。通过将您的文件保存到相应的媒体类型目录,系统的媒体扫描程序可以在系统中正确地归类您的文件(例如铃声在系统设置中显示为铃声而不是音乐)。
例如,以下方法在公共图片目录中创建了一个用于新相册的目录:
public File getAlbumStorageDir(String albumName) {
// Get the directory for the user's public pictures directory.
File file = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), albumName);
if (!file.mkdirs()) {
Log.e(LOG_TAG, "Directory not created");
}
return file;
}
保存应用私有文件
如果您正在处理的文件不适合其他应用使用(例如仅供您的应用使用的图形纹理或音效),则应该通过调用 getExternalFilesDir()
来使用外部存储上的私有存储目录。此方法还会采用 type
参数指定子目录的类型(例如 DIRECTORY_MOVIES
)。 如果您不需要特定的媒体目录,请传递 null
以接收应用私有目录的根目录。
从 Android 4.4 开始,读取或写入应用私有目录中的文件不再需要 READ_EXTERNAL_STORAGE
或 WRITE_EXTERNAL_STORAGE
权限。 因此,您可以通过添加 maxSdkVersion
属性来声明,只能在较低版本的 Android 中请求该权限:
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="18" />
...
</manifest>
注:当用户卸载您的应用时,此目录及其内容将被删除。此外,系统媒体扫描程序不会读取这些目录中的文件,因此不能从 MediaStore
内容提供程序访问这些文件。 同样,不应将这些目录用于最终属于用户的媒体,例如使用您的应用拍摄或编辑的照片或用户使用您的应用购买的音乐等 — 这些文件应保存在公共目录中。
有时,已分配某个内部存储器分区用作外部存储的设备可能还提供了 SD 卡槽。在使用运行 Android 4.3 和更低版本的这类设备时,getExternalFilesDir()
方法将仅提供内部分区的访问权限,而您的应用无法读取或写入 SD 卡。不过,从 Android 4.4 开始,可通过调用getExternalFilesDirs()
来同时访问两个位置,该方法将会返回包含各个位置条目的 File
数组。 数组中的第一个条目被视为外部主存储;除非该位置已满或不可用,否则应该使用该位置。 如果您希望在支持 Android 4.3 和更低版本的同时访问两个可能的位置,请使用支持库中的静态方法 ContextCompat.getExternalFilesDirs()
。 在 Android 4.3 和更低版本中,此方法也会返回一个 File
数组,但其中始终仅包含一个条目。
注意 尽管 MediaStore
内容提供程序不能访问 getExternalFilesDir()
和 getExternalFilesDirs()
所提供的目录,但其他具有 READ_EXTERNAL_STORAGE
权限的应用仍可访问外部存储上的所有文件,包括上述文件。 如果您需要完全限制您的文件的访问权限,则应该转而将您的文件写入到内部存储。
保存缓存文件
要打开表示应该将缓存文件保存到的外部存储目录的 File
,请调用 getExternalCacheDir()
。 如果用户卸载您的应用,这些文件也会被自动删除。
与前述 ContextCompat.getExternalFilesDirs()
相似,您也可以通过调用 ContextCompat.getExternalCacheDirs()
来访问辅助外部存储(如果可用)上的缓存目录。
1
提示:为节省文件空间并保持应用性能,您应该在应用的整个生命周期内仔细管理您的缓存文件并移除其中不再需要的文件,这一点非常重要。/p>