【问题标题】:Android FileProvider content:// uri no read permission in target appAndroid FileProvider content:// uri 在目标应用程序中没有读取权限
【发布时间】:2019-11-20 02:00:42
【问题描述】:

我尝试将我的 App1“external_files”目录 (/storage/emulated/0/Android/data/com.package.app1/files) 中存在的文件(PDF 类型,但这并不重要)共享给我的 App2 .我正在 Android 10 模拟器(来自 Android Studio,最新)中对此进行测试,这两个应用程序都使用 Scoped Storage,并且未启用对旧版外部存储的支持。

App1 清单中的所有内容似乎都由“书”定义:

    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="com.package.app1.fileprovider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/my_paths" />
    </provider>

那么 my_paths.xml 有

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-files-path name="external_files" path="." />
</paths>

然后在 App1 中:

Intent intent = new Intent(Intent.ACTION_SEND);
intent.setPackage("com.package.app2");
File file = new File(context.getExternalFilesDir(null), "MyFile.pdf");
Uri uri = FileProvider.getUriForFile(context, "com.package.app1.fileprovider", file);
context.grantUriPermission("com.package.app2", uri,
                        FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndTypeAndNormalize(uri, "application/pdf");
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivityForResult(intent, MY_REQUEST_CODE);

App2 在其清单中有:

<activity android:name=".MainActivity" android:configChanges="orientation|screenSize|keyboardHidden"
    android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar">
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="application/pdf" />
    </intent-filter>
</activity>

在 App2 中:活动 onCreate():

   Uri uri = getIntent().getData(); // The content:// uri looks correct
   if (uri != null) {
        DocumentFile docFile = DocumentFile.fromSingleUri(this, uri);
        Log.d("myTag", uri.toString + ": EXISTS: ", docFile.exists(), ", can read: ", docFile.canRead());
   }

无论我做什么,输出总是:

content://com.package.app1.fileprovider/external_files/MyFile.pdf: EXISTS: true, can read: false

我做错了什么,为什么我不能得到“can read: true”?

【问题讨论】:

  • canRead() 可靠吗?最好尝试阅读。打开输入流并读取。
  • context.grantUriPermission("com.package.app2... 尝试不使用该行。
  • 使用 setDataAndType() 代替。
  • @blackapps,谢谢 - 尝试打开输入流,但失败了。当我测试 docFile.isFile() 和 docFile.isDirectory() 时,两者都返回 false。但是 docFile 的 mUri 成员看起来是正确的: content://com.package.app1.fileprovider/external_files/MyFile.pdf
  • @blackapps,感谢您的帮助,也尝试了 setDataAndType() 代替,没有区别...

标签: android uri android-fileprovider scoped-storage


【解决方案1】:

@blackapps 在第一条评论中的建议是正确的:DocumentFile 上的 docFile.canRead() 不可靠,当 docFile.isFile() 返回 false 时(docFile.isDirectory() 也返回 false,这是常规的来自 FileProvider 的文件内容)。尽管 docFile.canRead() 返回 false,但我能够在 docFile 对象上打开 InputStream 并且可以很好地读取它。谢谢你,@blackapps!

【讨论】:

  • 这显然是 Android 的故障,或者至少是误导性的文档。我会尝试向开发人员或文档作者发送错误报告。
【解决方案2】:

实际上 DocumentFile 的 canRead() 是可靠的。但它检查的东西与 File.canRead() 不同

根据 DocumentsContractApi19,这就是 canRead() 对 DocumentFile 所做的:

 public static boolean canRead(Context context, Uri self) {
        // Ignore if grant doesn't allow read
        if (context.checkCallingOrSelfUriPermission(self, Intent.FLAG_GRANT_READ_URI_PERMISSION)
                != PackageManager.PERMISSION_GRANTED) {
            return false;
        }

        // Ignore documents without MIME
        if (TextUtils.isEmpty(getRawType(context, self))) {
            return false;
        }

        return true;
    }

我的建议是:不要像处理文件那样使用 canRead()。相反,尝试使用 try/catch 打开 uri 进行读取

【讨论】:

    猜你喜欢
    • 2017-09-24
    • 1970-01-01
    • 1970-01-01
    • 2011-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-23
    • 1970-01-01
    • 2016-05-30
    相关资源
    最近更新 更多