FileProvider冲突
模块化的目的是让多个模块或者app快速接入此模块功能。如果模块中使用了FileProvider来向外提供文件访问。可能会遇到一些问题:
- 模块可能依赖其他模块,其他模块中同样声明有FileProviver,造成资源合并冲突。
- 模块集成到宿主工程后,与主工程或者同样集成到宿主工程的其他模块造成冲突。
- 模块集成到多个app后,这些app,因为FileProvider冲突,无法同时安装。
构建时的资源冲突
问题1和2,本质上是同一个问题。即在资源文件合并时产生了冲突。
用户反馈模块中的拍照功能需要用到的FileProvider同集成的账号模块的FileProvider冲突了。
冲突之处
有哪些地方,在声明的时候会造成冲突呢?
android:authorities冲突
认识android:authorities
android:authorities声明的字符串相当于一个约定的key,在XML配置中的authorities同构造URI时,使用的authorities必须是相同的。官方文档建议字符串声明为Doamin+provider
. Set the
android:authoritiesattribute to a URI authority based on a domain you control; for example, if you control the domainmydomain.comyou should use the authoritycom.mydomain.fileprovider
其实该字符串可以任意,只需要XML和构造URI的时候是同一个字符串即可。可以看下系统的一些provider的定义
当然官方推荐的使用Domain,也是确保authorities和其他app中声明的不相同,否则会造成app冲突,无法安装。
所以我们在声明的时候,遵循推荐做法即可,一般是"${applicationId}.fileProvider"。如果发生了多个FileProvider的authorities声明的冲突,需要怎么解决呢?
解决冲突
不要使用tools:replace="android:authorities" 如上面的截图显示,错误提示中推荐使用tools:replace="android:authorities"来解决冲突。虽然编译冲突的问题可能能够解决,但是我们知道replace这种策略,会按照优先级替换配置。可能会造成其他模块的功能异常的情况。所以并不是解决问题之道。
面对这样的冲突,有两种解决方案
- 保持所有的FileProvider的authorities一致。
应该优先选择此种方案。比如统一使用"${applicationId}.fileProvider"。
val contentUri = FileProvider.getUriForFile(
context,
context.packageName,
currentFile
)
如果你希望同时安装app的多个变体,需要为每个变体配置不同的application id
如果不想使用application id,有一些小的技巧
- 在string.xml中配置。如需更改,可以在宿主工程中覆盖此字符串配置。
值得一提的是,比如定义的string.xml
<string name="authority" translatable="false">com.example.myapp.provoder</string>
在AndroidManifest中定义
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="@string/authority"
android:exported="false"
tools:replace="android:authorities"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
另外的一个模块的FileProvider定义为
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.example.myapp.provoder"
android:exported="false"
tools:replace="android:authorities"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
这样也是会造成冲突的。原因是在编译期间,并不会将@string/authority替换成真正的字符串资源,只会把它当成普通的字符串,所以两个字符串不相同,造成了冲突。
所以想要使用字符串资源的这种方式,就需要将所有的authorities统一成使用字符串资源。
- 定义接口获取字符串。这种情况,需要把provider的配置放到宿主工程。不推荐使用。
如果模块间无法做到统一。可以考虑第二种方案。
- 继承FileProvider,使用模块自己的FileFrovider
class SelfFileProvider : FileProvider()
<provider
android:name=".fileprovider.SelfFileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
meta-data resource 冲突
项目中的paths的配置文件名冲突。
解决冲突
- 推荐统一命名file_paths
- 参考authorities冲突的解决方案2,自定义FileProvider.