弄清楚INSTALL FAILED CONFLICTING PROVIDER问题

  • 内容
  • 评论
  • 相关

现象

安装应用的时候会提示安装失败,提示信息是INSTALL FAILED CONFLICTING PROVIDER,如下图所示:

究其原因

根据英文我们可以直观的知道因为provider的问题,在Android中provider的authority要求必须是唯一的,比如你在定义一个provider时需要为它指定一个唯一的authority。如果你在安装一个带有provider的应用时,系统会检查当前已安装应用的authority是否和你要安装应用的authority相同,如果相同则会弹出上述警告,并且安装失败。

这种问题多出现在接入第三方SDK、多个项目共用一个Module或Library,并且用到了provider。

解决方案

1. 简单粗暴

修改provider的authority,确保authority的命名唯一。这样以硬编码的形式进行修改,会很麻烦,每次新建项目添加已有的Module或Library都要修改他们包含的provider的authority。不建议这样使用硬编码的形式进行解决这个问题。这里就不过多陈述了。

2.以柔克刚

在定义provider的时候,authority使用软编码。这样即使多个项目引用同一个Module或Library,其定义的provider的名字都不用进行修改了。
举例:
1
2
3
4
5
6
7
8
9
<provider
    android:name="android.support.v4.content.FileProvider"
    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>
上述代码中通过${applicationId}.fileprovider的形式来指定provider的authorities,所以该provider的authorities会根据applicationId的不同而不同,从而避免了authorities的冲突问题。
接着大家心中会有疑问?那authorities的URI不能在代码里声明常量了,那怎么获取呢?只要肯想,还是有办法的。
直接上代码:
Java
1
2
3
4
5
6
7
8
9
10
11
public class ProviderUtils {
    public static String getAuthority(Context context, String  providerClassName) {
        try {
            ComponentName componentName = new ComponentName(context.getApplicationContext(), providerClassName);
            ProviderInfo providerInfo = context.getPackageManager().getProviderInfo(componentName, 0);
            return providerInfo.authority;
        } catch (PackageManager.NameNotFoundException e) {
            return providerClassName;
        }
    }
}
Kotlin
1
2
3
4
5
6
7
8
9
10
11
12
@JvmStatic
fun getAuthority(context: Context?, providerClassName: String): String {
    return context?.let {
        try {
            val componentName = ComponentName(context.applicationContext, providerClassName)
            val providerInfo = context.packageManager.getProviderInfo(componentName, 0)
            providerInfo.authority
        } catch (e: Exception) {
            providerClassName
        }
    } ?: ""
}
使用的时候代码展示如下:
1
FileProvider.getUriForFile(pContext, ProviderUtil.getAuthority(pContext, FileProvider::class.java.name), pFile)
OK,这样就一劳永逸了,再创建新的项目直接使用共用的Module或Library,就不用担心Provider冲突的问题了。
我在这里强烈建议大家使用以柔克刚的方案。
推荐阅读: