访问方法 | 所需权限 | 其他应用是否可以访问? | 卸载应用时是否移除文件? | |
---|---|---|---|---|
仅供您的应用使用的文件 | 从内部存储空间访问,可以使用 getFilesDir() 或 getCacheDir() 方法从外部存储空间访问,可以使用 getExternalFilesDir() 或 getExternalCacheDir() 方法 | 从内部存储空间访问不需要任何权限 如果应用在搭载 Android 4.4(API 级别 19)或更高版本的设备上运行,从外部存储空间访问不需要任何权限 | 否 | 是 |
可共享的媒体文件(图片、音频文件、视频) | MediaStore API | 在 Android 11(API 级别 30)或更高版本中,访问其他应用的文件需要 READ_EXTERNAL_STORAGE 在 Android 10(API 级别 29)中,访问其他应用的文件需要 READ_EXTERNAL_STORAGE 或 WRITE_EXTERNAL_STORAGE 在 Android 9(API 级别 28)或更低版本中,访问所有文件均需要相关权限 | 是,但其他应用需要 READ_EXTERNAL_STORAGE 权限 | 否 |
其他类型的可共享内容,包括已下载的文件 | 存储访问框架 | 无 | 是,可以通过系统文件选择器访问 | 否 |
访问权限限制
在 Android 11(API 级别 30)及更高版本中,您不能使用 ACTION_OPEN_DOCUMENT_TREE
intent 操作来请求访问以下目录:
- 内部存储卷的根目录。
- 设备制造商认为可靠的各个 SD 卡卷的根目录,无论该卡是模拟卡还是可移除的卡。可靠的卷是指应用在大多数情况下可以成功访问的卷。
Download
目录。
此外,在 Android 11(API 级别 30)及更高版本中,您不能使用 ACTION_OPEN_DOCUMENT_TREE
intent 操作来请求用户从以下目录中选择单独的文件:
Android/data/
目录及其所有子目录。Android/obb/
目录及其所有子目录。
创建新文件
// 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)
}
打开文件
// Request code for selecting a PDF document.
const val PICK_PDF_FILE = 2
fun openFile(pickerInitialUri: Uri) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/pdf"
// Optionally, specify a URI for the file that should appear in the
// system file picker when it loads.
putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
}
startActivityForResult(intent, PICK_PDF_FILE)
}
授予对目录内容的访问权限
fun openDirectory(pickerInitialUri: Uri) {
// Choose a directory using the system's file picker.
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
// Optionally, specify a URI for the directory that should be opened in
// the system file picker when it loads.
putExtra(DocumentsContract.EXTRA_INITIAL_URI, pickerInitialUri)
}
startActivityForResult(intent, your-request-code)
}
保留权限
val contentResolver = applicationContext.contentResolver
val takeFlags: Int = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
// Check for the freshest data.
contentResolver.takePersistableUriPermission(uri, takeFlags)
打开文档
val contentResolver = applicationContext.contentResolver
@Throws(IOException::class)
private fun getBitmapFromUri(uri: Uri): Bitmap {
val parcelFileDescriptor: ParcelFileDescriptor =
contentResolver.openFileDescriptor(uri, "r")
val fileDescriptor: FileDescriptor = parcelFileDescriptor.fileDescriptor
val image: Bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor)
parcelFileDescriptor.close()
return image
}
输入流
val contentResolver = applicationContext.contentResolver
@Throws(IOException::class)
private fun readTextFromUri(uri: Uri): String {
val stringBuilder = StringBuilder()
contentResolver.openInputStream(uri)?.use { inputStream ->
BufferedReader(InputStreamReader(inputStream)).use { reader ->
var line: String? = reader.readLine()
while (line != null) {
stringBuilder.append(line)
line = reader.readLine()
}
}
}
return stringBuilder.toString()
}
修改文档
val contentResolver = applicationContext.contentResolver
private fun alterDocument(uri: Uri) {
try {
contentResolver.openFileDescriptor(uri, "w")?.use {
FileOutputStream(it.fileDescriptor).use {
it.write(
("Overwritten at ${System.currentTimeMillis()}\n")
.toByteArray()
)
}
}
} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
}
删除文档
DocumentsContract.deleteDocument(applicationContext.contentResolver, uri)