可以通过 FileProvider 使用多个权限吗?
Possible to use multiple authorities with FileProvider?
背景
我维护着一个 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
然后 returns 为空,导致此 NPE。
如果我在此指令处暂停时手动调用 resolveContentProvider
并传递 "${applicationId}.fileprovider"
或 "${applicationId}.bugshaker.fileprovider"
、resolveContentProvider
returns 一个非空 ProviderInfo
实例(这似乎是预期的结果)。
这种差异让我感到困惑,因为 <provider>
element documentation 声明支持多个权限:
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.
问题
- 是否可以让单个应用程序公开具有多个权限和文件路径的
FileProvider
?
- 如果是这样,我需要更改什么才能使其正常工作?
- 如果没有,是否有其他方法可以在我的图书馆中配置文件共享来避免此类冲突?
我对这个问题的解决方案实际上是避免依赖单个 FileProvider
解析多个权限。虽然这并没有直接解决所述问题,但我将其发布以供后代使用。
我更新了我的库以利用 FileProvider
的空子类,因此库的更新清单提供程序条目现在是:
<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>
(1) 使用库存 FileProvider
和 (2) 使用我的库的应用程序的合并清单现在将包含下面显示的两个条目(没有冲突!):
<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>
直到一位同事指出,我才意识到这是一个潜在的解决方案。我之前(错误地)假设清单中的所有 FileProvider
都必须设置
android:name="android.support.v4.content.FileProvider"
但快速检查 the documentation 发现我的错误:
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"). [...]
我也遇到这个问题,用这个方法解决了。
就像我有一个使用文件提供程序的库图像选择器,而且我的应用程序在构建我的应用程序冲突时也使用文件提供程序。
我提供的文件是
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="org.contentarcadeapps.photoeditor.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
将此更改为
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="org.contentarcadeapps.photoeditor.fileprovider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"
tools:replace="android:resource"/>
</provider>
背景
我维护着一个 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
然后 returns 为空,导致此 NPE。
如果我在此指令处暂停时手动调用 resolveContentProvider
并传递 "${applicationId}.fileprovider"
或 "${applicationId}.bugshaker.fileprovider"
、resolveContentProvider
returns 一个非空 ProviderInfo
实例(这似乎是预期的结果)。
这种差异让我感到困惑,因为 <provider>
element documentation 声明支持多个权限:
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.
问题
- 是否可以让单个应用程序公开具有多个权限和文件路径的
FileProvider
?- 如果是这样,我需要更改什么才能使其正常工作?
- 如果没有,是否有其他方法可以在我的图书馆中配置文件共享来避免此类冲突?
我对这个问题的解决方案实际上是避免依赖单个 FileProvider
解析多个权限。虽然这并没有直接解决所述问题,但我将其发布以供后代使用。
我更新了我的库以利用 FileProvider
的空子类,因此库的更新清单提供程序条目现在是:
<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>
(1) 使用库存 FileProvider
和 (2) 使用我的库的应用程序的合并清单现在将包含下面显示的两个条目(没有冲突!):
<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>
直到一位同事指出,我才意识到这是一个潜在的解决方案。我之前(错误地)假设清单中的所有 FileProvider
都必须设置
android:name="android.support.v4.content.FileProvider"
但快速检查 the documentation 发现我的错误:
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"). [...]
我也遇到这个问题,用这个方法解决了。 就像我有一个使用文件提供程序的库图像选择器,而且我的应用程序在构建我的应用程序冲突时也使用文件提供程序。
我提供的文件是
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="org.contentarcadeapps.photoeditor.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"/>
</provider>
将此更改为
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="org.contentarcadeapps.photoeditor.fileprovider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths"
tools:replace="android:resource"/>
</provider>