标签: scoped-storage

当我只能处理文件或文件路径时如何处理 SAF?

背景

在 Android Q 之前,如果我们想要获取有关 APK 文件的信息,我们可以使用WRITE_EXTERNAL_STORAGEREAD_EXTERNAL_STORAGE来访问存储,然后在文件路径上使用PackageManager.getPackageArchiveInfo函数。

类似的情况也存在,例如在压缩文件上使用ZipFile 类,可能还有无数的框架 API 和第三方库。

问题

谷歌最近宣布了对 Android Q 的大量限制。

其中之一称为“范围存储”,当访问设备拥有的所有文件时,它会破坏存储权限。它可以让您处理媒体文件,或者使用非常受限的存储访问框架 ( SAF ),该框架不允许应用程序使用文件 API 和文件路径访问和使用文件。

当Android Q Beta 2发布时,它破坏了很多应用程序,包括谷歌的应用程序。原因是它默认打开,影响所有应用程序,无论它们是否针对 Android Q。

原因是许多应用程序、SDK 和 Android 框架本身 - 都经常使用文件 API。在许多情况下,它们也不支持 InputStream 或 SAF 相关的解决方案。一个例子就是我写的 APK 解析示例 (PackageManager.getPackageArchiveInfo)。

然而,在 Q beta 3 上,情况发生了一些变化,因此目标 Q 的应用程序将具有范围存储,并且有一个标志可以禁用它,但仍然像往常一样使用正常的存储权限和文件 API。遗憾的是,该标志只是暂时的(请阅读此处),因此它推迟了不可避免的事情。

我尝试过的

我已经尝试并发现了接下来的事情:

  1. 使用存储权限确实不允许我读取任何非媒体文件的文件(我想找到 APK 文件)。就好像这些文件不存在一样。

  2. 使用 SAF,我可以找到 APK 文件,并通过一些解决方法来找到其真实路径(链接此处),我注意到文件 API 可以告诉我该文件确实存在,但无法获取其大小,并且框架无法使用其路径getPackageArchiveInfo在这里写过这个

  3. 我尝试创建到该文件的符号链接(链接此处 …

android file storage-access-framework android-10.0 scoped-storage

5
推荐指数
1
解决办法
1705
查看次数

Android 11 ACTION_OPEN_DOCUMENT_TREE:将初始 URI 设置为文档文件夹

在 Android 11 中使用范围存储模型,我想让用户能够选择文件夹,从文档文件夹开始:

val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI,    ???     )
startActivityForResult(intent, OPEN_DIRECTORY_REQUEST_CODE,null)
Run Code Online (Sandbox Code Playgroud)

问题是,如何生成手机文档文件夹的正确 URI?(它位于 root / 中)在官方文档中,没有给出示例。我真的希望所有标准位置都有一些简洁的常量?

android scoped-storage android-11

4
推荐指数
2
解决办法
1万
查看次数

createChooser() 意图异常“需要导出提供程序,或 grantUriPermission()”

该应用程序需要与其他应用程序共享存储在cacheDir根目录中的PDF文件。该问题出现在 Android 12 上,也可能出现在其他版本上。

显现:

    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
Run Code Online (Sandbox Code Playgroud)

提供者路径:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <cache-path name="cache" path="." />
</paths>
Run Code Online (Sandbox Code Playgroud)

意图:

        val pdfFile = File(requireContext().cacheDir, pdfFileName)
        val fileUri: Uri = FileProvider.getUriForFile(
            requireContext().applicationContext,
            requireContext().packageName.toString() + ".provider",
            pdfFile
        )
        val intent = Intent()
        intent.action = Intent.ACTION_SEND
        intent.putExtra(Intent.EXTRA_STREAM, fileUri)
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
        intent.type = "application/pdf"
        startActivity(Intent.createChooser(intent, "Share Document"))
Run Code Online (Sandbox Code Playgroud)

共享表成功打开,但此时始终显示此异常,并且随后共享到另一个应用程序失败。

Writing exception to parcel
    java.lang.SecurityException: Permission Denial: reading 
androidx.core.content.FileProvider uri
content://uk.co.packagename.provider/cache/8BEDF7212-0DE46-42B0-9FA9-32C434BDD2F3HO.pdf
from pid=15363, uid=1000 requires the provider be exported, or grantUriPermission() …
Run Code Online (Sandbox Code Playgroud)

android android-contentprovider scoped-storage

4
推荐指数
1
解决办法
1462
查看次数

Scoped Storage - 下载文件夹访问

我的目标是 android 10,我不使用android:requestLegacyExternalStorage. 我用的是DownloadManager通过下载到下载文件夹:request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, fileName)。我收听下载完整广播,提取 uri ( file:///storage/emulated/0/Download/<name>.pdf) 并通过 .pdf 文件在 pdf 阅读器中打开它FileProvider。它有效......为什么?自 android 10 和范围存储以来,它不应该不起作用吗?为什么我可以访问下载文件夹中的文件?

编辑:Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).listFiles()列出所有文件,尽管文档说:

为了提高用户隐私,不推荐直接访问共享/外部存储设备。当应用以 Q 为目标时,此方法返回的路径不再可供应用直接访问

android android-download-manager storage-access-framework android-10.0 scoped-storage

3
推荐指数
1
解决办法
2433
查看次数

Android R – 使用 Scoped 存储 (MediaStore.Downloads) 在下载文件夹中创建一个文本文件

我的应用程序需要在下载文件夹中的文本文件中保存一个字符串。目前(目标:API 29 (Q) 我正在使用 FILE API 和:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
android:requestLegacyExternalStorage="true"
Run Code Online (Sandbox Code Playgroud)

但是对于 API 30 (R),如果我理解得很好,我必须将其迁移到作用域存储 (MediaStore.Downloads)。

在这里,我有点失落。我在下载中找不到显示如何创建文本文件的好的文档或片段。如果有人可以解释或展示如何做到这一点,我会很高兴吗?

android mediastore scoped-storage

3
推荐指数
1
解决办法
3403
查看次数

Android 11如何获取持久文件夹权限?

我的应用程序扫描某些目录中的文本文件。用户可以点击按钮来添加目录:

val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivityForResult(intent, RESULT_CODE_DOCUMENT_TREE)
Run Code Online (Sandbox Code Playgroud)

然后,我将 URI 存储在数据库中,并用于DocumentsContract扫描目录中的文件并读取它们。在应用程序启动时,我加载存储的 URI 并以与扫描文件相同的方式处理它们。

在 Android API 28 上,存储的 URI 工作正常。在 Android API 30 上,SecurityException如果我在应用程序重新启动后尝试访问其中任何一个,我会收到一个错误消息。

有没有办法让这些权限在现代 Android 上持久存在?或者我应该实施一种完全不同的方法?

android scoped-storage

2
推荐指数
1
解决办法
2796
查看次数

如何在 Android 11 或 Android Q 中保存视频文件

我想保存 Whatsapp 的状态。通过使用此代码,我可以保存图像

public void saveImage(Bitmap bitmap, String name) {
    OutputStream fileOutputStream;
    Uri imageUri;
    try {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            ContentResolver contentResolver = context.getApplicationContext().getContentResolver();
            ContentValues contentValues = new ContentValues();

            contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "" + name);
            contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpg");
            contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/V Troid/WhatsApp");
            imageUri = contentResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
            fileOutputStream = (FileOutputStream) contentResolver.openOutputStream(Objects.requireNonNull(imageUri));
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fileOutputStream);
            Objects.requireNonNull(fileOutputStream);
            Toast.makeText(context.getApplicationContext(), "Image Saved", Toast.LENGTH_SHORT).show();

        }
    } catch (Exception e) {
        Toast.makeText(context.getApplicationContext(), "Error \n" + e, Toast.LENGTH_SHORT).show();
    }
}
Run Code Online (Sandbox Code Playgroud)

但现在我也想保存视频文件,为此我正在使用此代码,但它对我不起作用,此代码损坏后我得到的文件

public void saveVideo(String name, Uri vidUri) {
        OutputStream …
Run Code Online (Sandbox Code Playgroud)

java android android-studio scoped-storage android-11

2
推荐指数
1
解决办法
1670
查看次数

如何使用 JAVA 在 Android 10 及更高版本的媒体商店中保存 pdf 文件?

更新:

为了保存PDF文件:

在下面的答案部分。

为了保存位图文件:

@RequiresApi(api = Build.VERSION_CODES.Q)
@NonNull
private Uri saveBitmap(@NonNull final Context context, @NonNull final Bitmap bitmap,
                       @NonNull final Bitmap.CompressFormat format, @NonNull final String mimeType,
                       @NonNull final String displayName, @Nullable final String subFolder) throws IOException {
    String relativeLocation = Environment.DIRECTORY_PICTURES;

    if (!TextUtils.isEmpty(subFolder)) {
        relativeLocation += File.separator + subFolder;
    }

    final ContentValues contentValues = new ContentValues();
    contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, displayName);
    contentValues.put(MediaStore.MediaColumns.MIME_TYPE, mimeType);
    contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, relativeLocation);

    final ContentResolver resolver = context.getContentResolver();

    OutputStream stream = null;
    Uri uri = null;

    try {
        final Uri contentUri …
Run Code Online (Sandbox Code Playgroud)

java pdf mediastore android-external-storage scoped-storage

1
推荐指数
1
解决办法
3506
查看次数