可以对 FileProvider 使用多个权限吗? [英] Possible to use multiple authorities with FileProvider?

查看:30
本文介绍了可以对 FileProvider 使用多个权限吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

我维护一个,其核心功能包括将通过编程捕获的屏幕截图分享给外部电子邮件应用程序.

我使用 FileProvider 来完成此操作,这意味着我的库的清单包含 一个标签:

<元数据android:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/filepaths"/></提供者>

filepaths.xml 定义如下:

<files-path path="bug-reports/" name="bug-reports"/></路径>

我的库的使用者有一个应用程序,它本身使用 FileProvider 来共享文件.我的期望是,如果消费应用程序使用以下清单 <provider> 标记,则应该可以允许两个提供者共享文件:

<元数据android:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/file_paths"工具:替换=机器人:资源"/></提供者>

此清单条目:

  • 指定两个 Provider 权限,${applicationId}.fileprovider(用于应用程序文件共享)和 ${applicationId}.bugshaker.fileprovider(用于库文件共享);
  • 引用更新的 filepaths.xml,其中包含应用程序生成的文件和库生成的文件的单独目录定义:

<外部路径名称=已编辑"路径=""/><文件路径名称=错误报告"路径=错误报告/"/></路径>

构建应用程序后,我们已确认生成的清单已将正确的节点替换为这些更新的值.

但是,当使用此配置的应用程序组装(成功)并运行时,我们会看到启动时崩溃:

E: FATAL EXCEPTION: main进程:com.stkent.bugshakertest,PID:11636java.lang.RuntimeException:无法获取提供程序 android.support.v4.content.FileProvider:java.lang.NullPointerException:尝试调用虚拟方法 'android.content.res.XmlResourceParser android.content.pm.PackageItemInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' 在一个空对象引用上在 android.app.ActivityThread.installProvider(ActivityThread.java:5856)在 android.app.ActivityThread.installContentProviders(ActivityThread.java:5445)在 android.app.ActivityThread.handleBindApplication(ActivityThread.java:5384)在 android.app.ActivityThread.-wrap2(ActivityThread.java)在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1545)在 android.os.Handler.dispatchMessage(Handler.java:102)在 android.os.Looper.loop(Looper.java:154)在 android.app.ActivityThread.main(ActivityThread.java:6119)在 java.lang.reflect.Method.invoke(Native Method)在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)引起:java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.PackageItemInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)'对象引用在 android.support.v4.content.FileProvider.parsePathStrategy(FileProvider.java:583)在 android.support.v4.content.FileProvider.getPathStrategy(FileProvider.java:557)在 android.support.v4.content.FileProvider.attachInfo(FileProvider.java:375)在 android.app.ActivityThread.installProvider(ActivityThread.java:5853)在 android.app.ActivityThread.installContentProviders(ActivityThread.java:5445)在 android.app.ActivityThread.handleBindApplication(ActivityThread.java:5384)在 android.app.ActivityThread.-wrap2(ActivityThread.java)在 android.app.ActivityThread$H.handleMessage(ActivityThread.java:1545)在 android.os.Handler.dispatchMessage(Handler.java:102)在 android.os.Looper.loop(Looper.java:154)在 android.app.ActivityThread.main(ActivityThread.java:6119)在 java.lang.reflect.Method.invoke(Native Method)在 com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)

使用调试器,我可以看到方法 FileProvider.parsePathStrategy 调用了 PackageManager.resolveContentProvider 带有权限字符串 "${applicationId}.fileprovider;${applicationId}.bugshaker.fileprovider".resolveContentProvider 然后返回 null,导致这个 NPE.

如果我在此指令暂停时手动调用 resolveContentProvider 并传递 "${applicationId}.fileprovider" "${applicationId}.bugshaker.fileprovider", resolveContentProvider 而是返回一个非空的 ProviderInfo 实例(这似乎是预期的结果).>

这种差异让我感到困惑,因为 元素文档 声明支持多个权限:

<块引用>

一个或多个 URI 授权列表,用于标识内容提供商提供的数据.多个权威通过用分号分隔它们的名称来列出.为避免冲突,权限名称应使用 Java 风格的命名约定(例如 com.example.provider.cartoonprovider).通常,它是实现提供者的 ContentProvider 子类的名称

没有默认值.必须至少指定一个权限.

问题

  • 是否可以让单个应用程序公开具有多个权限和文件路径的 FileProvider?
    • 如果是这样,我需要改变什么才能使其发挥作用?
    • 如果没有,是否还有其他方法可以在我的库中配置文件共享来避免此类冲突?

解决方案

我对这个问题的解决方案实际上是避免依赖单个 FileProvider 解析多个权限.虽然这并没有直接解决所述问题,但我将其发布以供后代使用.

<小时>

我更新了我的库以利用 FileProvider 的一个空子类,以便库的更新清单提供程序条目现在是:

<元数据android:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/library_file_paths"/></提供者>

(1) 使用库存 FileProvider 和 (2) 使用我的库的应用程序的合并清单现在将包含下面显示的两个条目(无冲突!):

</提供者><提供者android:name="com.github.stkent.bugshaker.flow.email.screenshot.BugShakerFileProvider"android:authorities="com.consumer.application.bugshaker.fileprovider"机器人:出口=假"android:grantUriPermissions="true" ><元数据android:name="android.support.FILE_PROVIDER_PATHS"android:resource="@xml/library_file_paths"/></提供者>

直到一位同事指出,我才意识到这是一个潜在的解决方案.我之前的假设(并且错误地)是清单中的所有 FileProvider 都必须设置

android:name="android.support.v4.content.FileProvider"

但是快速检查文档发现了我的错误:

<块引用>

实现内容提供者的类的名称,是内容提供者的子类.这应该是一个完全限定的类名(例如,com.example.project.TransportationProvider").[...]

Background

I maintain a library whose core functionality involves sharing programmatically-captured screenshots to external email applications.

I use a FileProvider to accomplish this, which means my library's manifest contains a <provider> tag:

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.bugshaker.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/filepaths" />
</provider>

filepaths.xml is defined as follows:

<paths>
    <files-path path="bug-reports/" name="bug-reports" />
</paths>

A consumer of my library has an application which itself uses a FileProvider to share files. My expectation was that it should be possible to allow both providers to share files if the consuming application used the following manifest <provider> tag:

<provider
    android:authorities="${applicationId}.fileprovider;${applicationId}.bugshaker.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true"
    android:name="android.support.v4.content.FileProvider"
    tools:replace="android:authorities">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_paths"
        tools:replace="android:resource" />
</provider>

This manifest entry:

  • specifies two Provider authorities, ${applicationId}.fileprovider (for application file sharing) and ${applicationId}.bugshaker.fileprovider (for library file sharing);
  • references an updated filepaths.xml, which contains separate directory definitions for application-generated files and library-generated files:

<paths>
    <external-path
        name="redacted"
        path="" />
    <files-path
        name="bug-reports"
        path="bug-reports/" />
</paths>

After building the application, we have confirmed that the generated manifest has had the correct nodes replaced with these updated values.

However, when the application using this configuration is assembled (successfully) and run, we see a crash on launch:

E: FATAL EXCEPTION: main
   Process: com.stkent.bugshakertest, PID: 11636
   java.lang.RuntimeException: Unable to get provider android.support.v4.content.FileProvider: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.PackageItemInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
       at android.app.ActivityThread.installProvider(ActivityThread.java:5856)
       at android.app.ActivityThread.installContentProviders(ActivityThread.java:5445)
       at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5384)
       at android.app.ActivityThread.-wrap2(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1545)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:154)
       at android.app.ActivityThread.main(ActivityThread.java:6119)
       at java.lang.reflect.Method.invoke(Native Method)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
    Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.XmlResourceParser android.content.pm.PackageItemInfo.loadXmlMetaData(android.content.pm.PackageManager, java.lang.String)' on a null object reference
       at android.support.v4.content.FileProvider.parsePathStrategy(FileProvider.java:583)
       at android.support.v4.content.FileProvider.getPathStrategy(FileProvider.java:557)
       at android.support.v4.content.FileProvider.attachInfo(FileProvider.java:375)
       at android.app.ActivityThread.installProvider(ActivityThread.java:5853)
       at android.app.ActivityThread.installContentProviders(ActivityThread.java:5445) 
       at android.app.ActivityThread.handleBindApplication(ActivityThread.java:5384) 
       at android.app.ActivityThread.-wrap2(ActivityThread.java) 
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1545) 
       at android.os.Handler.dispatchMessage(Handler.java:102) 
       at android.os.Looper.loop(Looper.java:154) 
       at android.app.ActivityThread.main(ActivityThread.java:6119) 
       at java.lang.reflect.Method.invoke(Native Method) 
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) 
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) 

Using the debugger, I am able to see that the method FileProvider.parsePathStrategy invokes PackageManager.resolveContentProvider with the authority string "${applicationId}.fileprovider;${applicationId}.bugshaker.fileprovider". resolveContentProvider then returns null, leading to this NPE.

If I manually call resolveContentProvider while paused at this instruction and pass either "${applicationId}.fileprovider" or "${applicationId}.bugshaker.fileprovider", resolveContentProvider instead returns a non-null ProviderInfo instance (which would seem to be the expected result).

This difference confuses me because the <provider> element documentation states that multiple authorities are supported:

A list of one or more URI authorities that identify data offered by the content provider. Multiple authorities are listed by separating their names with a semicolon. To avoid conflicts, authority names should use a Java-style naming convention (such as com.example.provider.cartoonprovider). Typically, it's the name of the ContentProvider subclass that implements the provider

There is no default. At least one authority must be specified.

Questions

  • Is it possible to have a single application expose a FileProvider with multiple authorities and file paths?
    • If so, what do I need to change to make that work?
    • If not, are there other ways to configure file sharing within my library that avoid conflicts such as this one?

解决方案

My solution to this problem has actually been to avoid relying on a single FileProvider parsing multiple authorities. While this doesn't directly address the question as stated, I'm posting it for posterity.


I updated my library to leverage an empty subclass of FileProvider, so that the library's updated manifest provider entry is now:

<provider
    android:name=".flow.email.screenshot.BugShakerFileProvider"
    android:authorities="${applicationId}.bugshaker.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/library_file_paths" />
</provider>

The merged manifest of an application that (1) uses a stock FileProvider and (2) consumes my library will now contain both of the entries shown below (no collision!):

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.consuming.application.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true" >
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/application_file_paths" />
</provider>

<provider
    android:name="com.github.stkent.bugshaker.flow.email.screenshot.BugShakerFileProvider"
    android:authorities="com.consuming.application.bugshaker.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true" >
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/library_file_paths" />
</provider>

I didn't realize that this was a potential solution until a coworker pointed it out. My assumption had previously (and incorrectly) been that all FileProviders in the manifest must set

android:name="android.support.v4.content.FileProvider"

but a quick check of the documentation revealed my error:

The name of the class that implements the content provider, a subclass of ContentProvider. This should be a fully qualified class name (such as, "com.example.project.TransportationProvider"). [...]

这篇关于可以对 FileProvider 使用多个权限吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆