背景
我维护一个库,其核心功能是将程序捕获的截图共享到外部电子邮件应用程序。
为此,我使用了FileProvider
,这意味着我的库的清单包含<provider>
标记:
<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
的定义如下:
<paths>
<files-path path="bug-reports/" name="bug-reports" />
</paths>
我的库的使用者有一个应用程序,它本身使用FileProvider
来共享文件。我的期望是,如果使用以下清单<provider>
标签的消费应用程序,应该可以允许两个提供者共享文件:
<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>
该清单条目:
- 指定了两个
Provider
权限:${applicationId}.fileprovider
(用于应用文件共享)和${applicationId}.bugshaker.fileprovider
(用于库文件共享); - 引用了更新的
filepaths.xml
,其中包含应用程序生成文件和库生成文件的分别定义的目录:
<paths>
<external-path
name="redacted"
path="" />
<files-path
name="bug-reports"
path="bug-reports/" />
</paths>
应用程序构建完成后,我们确认生成的清单已使用这些更新值替换了正确的节点。
然而,当使用此配置运行应用程序进行组装(成功)并启动时,我们看到了一个崩溃:
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)
使用调试器,我能够看到方法FileProvider.parsePathStrategy
使用权限字符串"${applicationId}.fileprovider;${applicationId}.bugshaker.fileprovider"
调用PackageManager.resolveContentProvider
。然而,resolveContentProvider
返回null,导致了空指针异常。如果我在此处暂停并手动调用
resolveContentProvider
,并传递"${applicationId}.fileprovider"
或"${applicationId}.bugshaker.fileprovider"
之一,则resolveContentProvider
返回非空的ProviderInfo
实例(这似乎是预期的结果)。这种差异让我感到困惑,因为
<provider>
元素文档指出支持多个权限:“一个或多个URI身份证明,它们标识由内容提供者提供的数据。使用分号将其名称分隔开以列出多个身份证明。为避免冲突,身份证明名称应使用Java样式命名约定(例如com.example.provider.cartoonprovider)。通常,它是实现提供程序的ContentProvider子类的名称。”
“没有默认值。必须指定至少一个身份证明。”
问题:
1. 是否可能一个应用程序使用单个
FileProvider
公开多个权限和文件路径?- 如果可以,我需要改变什么才能使它工作? - 如果不行,有没有其他的方式来配置我的库中的文件共享,以避免这种冲突?
loadXmlMetaData()
方法中,您没有提供authority字符串,在FileProvider源代码中也没有看到这一点。前一行提供了一个authority给resolveContentProvider()
方法。您是指这个吗?如果是的话,ProviderInfo
提供了分号分隔的列表,FileProvider
似乎无法处理它。 - CommonsWareFileProvider
中的代码时,似乎他们没有处理多权限场景。他们有钩子来处理按权限分组的多路径策略,但似乎从未解析过分号分隔的列表。可能是未经测试的。我在我的StreamProvider
中编写了解析列表的代码,但我也没有测试它。 :-( - CommonsWareContentProvider
,更少的是可能由2个或更多库或1个其他库和应用程序本身以具体形式(即以注册形式)需要它。因此,我认为不存在 "通常情况"。在这种情况下,FileProvider
(和StreamProvider
)依赖于静态缓存数据来管理多个权限,因此仅使用FileProvider
/StreamProvider
的简单子类是不够的。 - CommonsWare