最近在开发时,需要下载一个文件,但是Android上的getexternalstoragepublicdirectory显示已过期(deprecated)。于是谷歌了一下,搜到了这篇文章从共享存储空间访问文档和其他文件。
原来,现在的Android系统,将文件系统划分为下面几项:
- 应用专属存储空间
- 共享存储
- 偏好设置
- 数据库
具体可以查看数据和文件存储概览,文章里有非常详细的介绍。这里主要看应用共享存储空间与共享存储。其中应用专属存储空间无需申请权限,在应用卸载的时候会移除文件。如果文件存储在外部存储空间中的目录内,其他应用也可以访问。而共享存储需要权限,在 Android 10(API 级别 29)或更高版本中,访问其他应用的文件需要 READ_EXTERNAL_STORAGE
或 WRITE_EXTERNAL_STORAGE
权限。在 Android 9(API 级别 28)或更低版本中,访问所有文件均需要相关权限,其中存储的文件不会随着应用的卸载而被移除。
所以我考虑将用户无需长期保存的文件存储在应用专属存储空间的外部存储空间中,这样的好处是无需申请权限,可以使用getExternalFilesDir()
或 getExternalCacheDir()
方法来获取路径。
以前在开发中,都是通过path来定位文件,而在Android 4.4(API 级别 19)及更高版本的设备上,可以使用存储访问框架与包括外部存储卷和云端存储空间在内的文档提供器互动。也就是从共享存储空间访问文档和其他文件里的主要内容。并且现在采用uri来对文件进行操作,File也可替换成DocumentFile。
存储访问框架支持以下访问文件和其他文档的用例。
-
ACTION_CREATE_DOCUMENT
intent 操作支持用户将文件保存在特定位置。 -
ACTION_OPEN_DOCUMENT
intent 操作支持用户选择要打开的特定文档或文件。 -
ACTION_OPEN_DOCUMENT_TREE
intent 操作在 Android 5.0(API 级别 21)及更高版本中提供,支持用户选择特定目录,授予应用对该目录中所有文件和子目录的访问权限。
具体代码可以查看文档,需要注意的是,现在需要调用startActivityForResult
加载系统文件选择器选择相对应的文件夹。比如系统Download文件夹的uri为Uri.parse(Environment.DIRECTORY_DOWNLOADS)
。新建文件的代码如下:
// Request code for creating a PDF document.
const val CREATE_FILE = 1
private fun createFile(pickerInitialUri: Uri) {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/pdf"
putExtra(Intent.EXTRA_TITLE, "invoice.pdf")
// Optionally, specify a URI for the directory that should be opened in
// the system file picker before your app creates the document.
putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
}
startActivityForResult(intent, CREATE_FILE)
}
检索新建项目的 URI
override fun onActivityResult(
requestCode: Int, resultCode: Int, resultData: Intent?) {
if (requestCode == your-request-code
&& resultCode == Activity.RESULT_OK) {
// The result data contains a URI for the document or directory that
// the user selected.
resultData?.data?.also { uri ->
// Perform operations on the document using its URI.
}
}
}
其他操作基本相同,文档里都有详细描述。随着Android版本的升高,对文件的管理也越来越严格,以后还得多参考官方文档进行操作。