如何解决Android 10的存储混乱

时间:2019-09-05 15:57:25

标签: android file android-fileprovider storage-access-framework scoped-storage

由于Android在有关文件访问的不同主要版本之间非常不一致,因此我感到有些失落。 我尝试尽可能简单地描述问题:

我的公司使用商业本机DRM保护我们提供的其他本机库。我们有一个许可应用程序,该应用程序调用了一些Voodoo来结束诸如/ sdcard / companyname / LicenseContainer /中的许可文件。其他受保护的应用程序以本机代码查看此目录,检查用户是否具有有效的许可证。

但是,Android 10更新完全无效了此工作流程,因为它仅提供scoped storage access。我们可以使用Storage Manager来授予访问权限,但是很遗憾,现在也已弃用该访问权限。

现在我的问题是: 一个应用程序如何将文件保存到/ sdcard / FOLDER上的某个位置

  1. 在删除应用程序时未删除
  2. 其他应用
  3. 可以使用本机代码访问

我对所有可能的解决方案(SAF,FileProvider等)有点不知所措,这些解决方案经常调用一个应用程序向另一个应用程序授予权限的问题。但是文件应该不需要安装的第一个应用程序就可以访问。

我知道必须有解决方案,因为最近的FileManager(即Files by Google)可以访问整个/sdcard/目录。

在不调用android:requestLegacyExternalStorage="true"之类的“ hacks”的情况下,最简单,最适合未来的路线是什么?

2 个答案:

答案 0 :(得分:0)

您可能会要求用户授予您访问任何文件或目录的权限,包括内部存储器或外部SD卡的根目录。您可以将该访问权限永久保留给您的应用程序,之后可以使用Scoped Storage API在任何地方读取/写入文件,直到该应用程序被卸载或重置。

然后,如果您需要使用本机C / C ++代码读取或写入文件,则可以获取文件的Linux文件描述符(整数),并将其传递给本机代码以与例如fdopen()调用一起使用。

这是一个Java代码段,用于从单个文件Uri(其字符串形式类似于content:// ...)获得文件描述符。

    ParcelFileDescriptor parcelFileDescriptor =
            getContentResolver().openFileDescriptor(uri, "r"); // gets FileNotFoundException here, if file we used to have was deleted
    int fd = parcelFileDescriptor.getFd(); // detachFd() if we want to close in native code

如果您具有本机库的源代码,或者可以使用C FILE *进行调用-它将可以正常工作。唯一的问题是当您没有源代码并且他们期望文件路径/名称时。 *更新* :仍然可以使用路径/文件名字符串传递给需要文件名的C / C ++函数。只需创建一个指向符号链接的名称,而不是“真实路径/文件名”,即可:

// fd is file descriptor obtained in Java code above
char fileName[32];
sprintf(fileName, "/proc/self/fd/%d", fd);
// The above fileName can be passed to C/C++ functions that expect a file name.
// They can read or write to it, depending on permissions to fd given in Java,
// but I guess C/C++ code can not create a new file. Someone correct me,
// if I'm mistaken here.

但是,这时我不确定,当您以这种方式在应用程序“沙盒”之外的目录中创建文件时,如果系统在卸载后也删除了该文件...是否需要写一个在Android 10上进行快速测试以找出答案,然后我们仍然不知道Google将来是否不会更改此行为。

答案 1 :(得分:0)

如果要将文件保存在共享存储中(用户和其他应用可以在其中访问文件),则需要使用

  • 媒体文件(图像,视频,音频,下载)使用 MediaStore
  • 对于文档和其他文件,使用 Storage Access Framework (这只是系统文件选择器)

例如,您可以使用以下代码段通过Storage Access Framework保存pdf文件

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)
}

用户选择目录后,我们仍然需要在onActivityResult方法中处理结果Uri。

override fun onActivityResult(
        requestCode: Int, resultCode: Int, resultData: Intent?) {
    if (requestCode == CREATE_FILE && resultCode == Activity.RESULT_OK) {
        // The result data contains a URI for directory that
        // the user selected.
        resultData?.data?.also { uri ->
            // save your data using the `uri` path
        }
    }
}

您可以在以下博客文章中详细了解此内容

https://androidexplained.github.io/android/android11/scoped-storage/2020/09/29/file-saving-android-11.html