【发布时间】:2019-08-27 21:11:36
【问题描述】:
背景
我维护一个library,其核心功能包括将通过编程捕获的屏幕截图分享给外部电子邮件应用程序。
我使用FileProvider 来完成此操作,这意味着我的库的清单包含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定义如下:
<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,导致这个 NPE。
如果我在这条指令处暂停时手动调用resolveContentProvider 并传递"${applicationId}.fileprovider" 或 "${applicationId}.bugshaker.fileprovider",resolveContentProvider 会返回一个非空的ProviderInfo 实例(看起来是预期的结果)。
这种差异让我感到困惑,因为<provider> element documentation 声明支持多个权限:
标识内容提供者提供的数据的一个或多个 URI 权限的列表。通过用分号分隔它们的名称来列出多个权威。为避免冲突,权限名称应使用 Java 风格的命名约定(例如 com.example.provider.cartoonprovider)。通常,它是实现提供程序的 ContentProvider 子类的名称
没有默认值。必须至少指定一个权限。
问题
- 是否可以让单个应用程序公开具有多个权限和文件路径的
FileProvider?- 如果是这样,我需要进行哪些更改才能使其正常工作?
- 如果没有,是否有其他方法可以在我的库中配置文件共享以避免此类冲突?
【问题讨论】:
-
"我可以看到 PackageItemInfo.loadXmlMetaData 方法正在使用权限字符串“${applicationId}.fileprovider;${applicationId}.bugshaker.fileprovider”被调用——你没有给
loadXmlMetaData()提供一个权限字符串,我在FileProvider源代码中没有看到。上一行给resolveContentProvider()提供了一个权限。是这个意思吗?如果是,ProviderInfo是提供分号分隔的列表,FileProvider似乎无法处理。 -
除此之外,查看
FileProvider中的代码,似乎他们没有处理多权限方案。它们具有按权限制定多种路径策略的钩子,但它们似乎从未解析分号分隔的列表。可能未经测试。我在myStreamProvider中有可以解析列表的代码,但我也没有对其进行测试。 :-( -
好的,更新了。当
attachInfo调用它的方法提供一个实例时,parsePathStrategy检索一个新的ProviderInfo实例似乎很奇怪。我看到ContentProviderdoes split the authorities inattachInfo和FileProvider调用了super,但是ContentProvider中的multi-authorities 字段似乎根本无法被子类访问。 -
可能是一个基本问题,但是需要不同类型内容提供者的应用程序通常在清单中做什么?他们是否需要创建一个“基础”内容提供者并适当地拥有该委托?
-
密切关注this issue。
标签: android android-manifest android-contentprovider manifest-merging