在 Android 11 上写入许多文件

Writing many files on Android 11

场景

我正在尝试创建许多文件,作为用户的一项功能。例如,我可能会编写一个应用程序,为他们在过去几周内收听的每首歌曲创建一个文件(例如,带有歌词的文本文件)。我不能让用户为我生成的每个文件选择目录和文件名,这会花费他们几个小时。用户应该可以从其他应用程序访问这些文档。

解决方案(不是真的)

在 Android11 中,存储访问框架似乎不会有用。我注意到有 2 个最初有趣的选项:

  1. 创建一个新文件(创建启动 activity 用户与之交互以仅保存一个文件),描述 here
  2. 授予对目录内容的访问权限(允许读取访问权限但不能写入任何文档),如 here.
  3. 所述
  4. 注意: 如果您以 Android 10 为目标并请求 WRITE_EXTERNAL_STORAGE,则解决方案已经存在。不幸的是,此权限在 Android 11 上被拒绝。因此,我无法使用张贴在 here.
  5. 上的解决方案
  6. 使用存储访问框架(特别是 ACTION_OPEN_DOCUMENT_TREE)获取对目录的访问权限以获取对目录的访问权限,但我无法将文件写入该目录。 (我得到 FileNotFoundException,因为这不是普通路径,而是树路径。)

判决

我想在我的情况下,我需要沿着“管理存储设备上的所有文件”的路线前进,如 here 所述,这涉及启动额外的 activity 和 ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION.我宁愿不请求读取 整个 驱动器的权限,也不想让用户进入设置应用程序授予权限。写入文件不需要任何权限...

您如何处理为用户保存许多(非媒体)文件?

在 Android 11 上,您可以使用标准文件 类 在 public 目录文档中创建子目录,就像在 10 以下一样。如果您知道如何操作,也可以在 10 中创建子目录。然后在该子目录中创建文件。不客气。

您也有很多其他 public 目录的写入权限。

您不需要为此访问所有文件。

用户可以使用设备上的官方文件应用访问您以这种方式创建的文件。

有 2 种方法,但首先,我将定义文件和目录名称,稍后我会将其保存在外部 (sdcard) 下载文件夹中

val outputFilename = "my_file"
val outputDirectory = "my_sub_directory" // The folder within the Downloads folder, because we use `DIRECTORY_DOWNLOADS`

有效但已弃用的方法:

虽然 Environment.getExternalStoragePublicDirectory 已被弃用,但它仍然适用于 Android 11 上的应用定位和 运行。

file = File(Environment.getExternalStoragePublicDirectory(
    Environment.DIRECTORY_DOCUMENTS),
    "$outputDirectory/$outputFilename"
)
val outputStream = FileOutputStream(file)

MediaStore方式:

或者,您可以使用 MediaStore 的 Files collection, suggested Gaurav Mall 。我不知道 MediaStore files 系列...

我用kotlin重写了这里修改写文件:

val resolver = context.contentResolver
val values = ContentValues()
// save to a folder
values.put(MediaStore.MediaColumns.DISPLAY_NAME, outputFilename)
values.put(MediaStore.MediaColumns.MIME_TYPE, "application/my-custom-type")
values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/" + outputDirectory)
val uri = resolver.insert(MediaStore.Files.getContentUri("external"), values)
// You can use this outputStream to write whatever file you want:
val outputStream = resolver.openOutputStream(uri!!)
// Or alternatively call other methods of ContentResolver, e.g. .openFile

在以下两种情况下,Android 29 岁及以上的人不需要 WRITE_EXTERNAL_STORAGE