多个应用使用相同的内容提供者

我正在开发一系列只在特定品牌中才能出现的应用程序(请考虑不同的运动队)。 然而,我遇到了一个问题,我正在使用一个库项目的所有专门品牌的应用程序,并希望使用相同的ContentProvider所有。 当我创buildContentProvider时,我将AUTHORITY声明为类中的常量(根据开发示例代码),并且我在清单文件中的每个特定应用程序中使用相同的权限。 它看起来像我不能在每个应用程序使用相同的权限,因为当我试图安装第二个应用程序(我安装一个品牌的一个很好,但第二次安装)时,我得到这个错误:

WARN/PackageManager(66): Can't install because provider name com.xxx.Provider (in package com.xxx) is already used by com.zzz 

我已经尝试了几种方法,但似乎没有一个工作。 我还没有做的一个想法是创build一个库jar,只是省略了Provider类,并在每个特定的应用程序中进行自定义。 任何想法如何解决这个问题,而不诉诸于?

Solutions Collecting From Web of "多个应用使用相同的内容提供者"

ContentProvider是由权威机构确定的,因此它必须是唯一的。 我不认为这有什么花招。

此外,Android平台还存在一个错误,即使它们具有不同的权限并被包含在单独的APK中,也会阻止为两个不同的ContentProviders使用相同的类名。 看到这里的错误。

我build议你的解决scheme是在你的库项目中创build抽象提供者类,然后在每个单独的应用程序中用一个唯一的名称来扩展它。 为了实现这一点,您可能需要创build一个脚本来生成/修改个别清单和内容提供者类。

希望这可以帮助。

这是一个古老的问题,但我最近在做类似的事情。 build立口味,现在是非常简单的。

在Gradle文件中指定BuildConfigField:

  productFlavors { free { applicationId "com.example.free" buildConfigField 'String', 'AUTHORITY', '"com.example.free.contentprovider"' } paid { applicationId "com.example.paid" buildConfigField 'String', 'AUTHORITY', '"com.example.paid.contentprovider"' } 

在清单中指定提供者权限:

  <provider android:name=".ContentProvider" android:authorities="${applicationId}.contentprovider" /> 

使用BuildConfigFieldvariables在提供者中设置权限:

  public static final String AUTHORITY = BuildConfig.AUTHORITY 

让我们说你的库包是com.android.app.library免费包是com.android.app.free支付包是com.android.app.paid

在你的免费项目和付费项目中,在一个可以是任何东西的软件包中创build一个相同的文件,但必须是相同的。

例:

  1. 使用com.android.app.data在您的免费版本中创build一个新的包

  2. 创build一个名为Authority.java的文件,并在里面(Authority.java)放:

    public class Authority {

     `public static final String CONTENT_AUTHORITY = "YOUR PROVIDER";` 

    }

  3. 重复这个付费版本,记得保持包名称相同和类名称。

现在,在您的合同文件中,在您的库中使用以下内容:

 public static String AUTHORITY = initAuthority(); private static String initAuthority() { String authority = "something.went.wrong.if.this.is.used"; try { ClassLoader loader = Contract.class.getClassLoader(); Class<?> clz = loader.loadClass("com.android.app.data.Authority"); Field declaredField = clz.getDeclaredField("CONTENT_AUTHORITY"); authority = declaredField.get(null).toString(); } catch (ClassNotFoundException e) {} catch (NoSuchFieldException e) {} catch (IllegalArgumentException e) { } catch (IllegalAccessException e) { } return authority; } public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); 

现在你应该可以使用两个权限。

信用:Ian Warick(编写代码) Android – 在应用程序项目中拥有提供者权限声明:我也在此处发布: Android重复提供者权限问题 – 不确定是否允许使用相同的答案回答相同types的问题。

可以使用以下方法将ContentProvider打包到库中,并在运行时设置ContentProvider的权限,以便在ContentProvider权限冲突的情况下将其包含到多个项目中。 这是有效的,因为真正的“权威”来自AndroidManifest …而不是ContentProvider类。

从基本的ContentProvider实现开始.AUTHORITY,CONTENT_URI和UriMatcher是静态的,但不是“最终的”….

 public class MyContentProvider extends ContentProvider { public static String AUTHORITY = "com.foo.bar.content"; public static Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); protected static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); 

然后,重写'attachInfo'方法,这样当ContentProvider首次被初始化时,你的ContentProvider将被从AndroidManifest收集到的ProviderInfo调用。 这将在进行任何可能的查询之前发生,很可能在初始应用程序类设置期间。 使用此机会将AUTHORITY,CONTENT_URI和UriMatcher重置为其“真实”值,如使用ContentProvider库的应用程序所提供的。

  @Override public void attachInfo(Context context, ProviderInfo info) { super.attachInfo(context, info); AUTHORITY = info.authority; CONTENT_URI = Uri.parse("content://" + AUTHORITY); uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(AUTHORITY, AlarmTable.TABLENAME, ALARMS); uriMatcher.addURI(AUTHORITY, AttributeTable.TABLENAME, ATTRIBUTES); uriMatcher.addURI(AUTHORITY, DeepLinkTable.TABLENAME, DEEPLINKS); uriMatcher.addURI(AUTHORITY, NotificationTable.TABLENAME, NOTIFICATIONS); uriMatcher.addURI(AUTHORITY, MetaDataTable.TABLENAME, RESOURCE_METADATA); uriMatcher.addURI(AUTHORITY, ResourceTable.TABLENAME, RESOURCES); uriMatcher.addURI(AUTHORITY, ResourceAttributeTable.TABLENAME, RESOURCES_ATTRIBUTES); uriMatcher.addURI(AUTHORITY, ResourceTagTable.TABLENAME, RESOURCES_TAGS); uriMatcher.addURI(AUTHORITY, TagTable.TABLENAME, TAGS); uriMatcher.addURI(AUTHORITY, UserTagTable.TABLENAME, USER_TAGS); uriMatcher.addURI(AUTHORITY, UserTable.TABLENAME, USERS); uriMatcher.addURI(AUTHORITY, CUSTOM, RAW); } 

当应用程序启动时,ContentProvider实际上是与Application类一起实例化的,所以它可以访问所有需要的包信息。 ProviderInfo对象将包含AndroidManifest中提供的信息…包含在最终应用程序中的列表。

  <provider android:authorities="com.foo.barapp.content" android:name="com.foo.bar.MyContentProvider"/> 

pipe理局现在将被重写为“com.foo.barapp.content”而不是默认值,并且UriMatcher将被更新为应用程序的值而不是默认值。 依赖于“AUTHORITY”的类现在将访问更新的值,并且UriMatcher将正确地区分“com.foo.barapp.content”的传入查询。

我已经同时testing了这两个示例应用程序和一个androidTest包,并发现它能正常工作。

您可以!

正如在这篇文章中所说( 这个文章解释了Firebase如何初始化它的库,而没有从你的Application#onCreate()方法给它一个上下文),你可以在清单中使用一个占位符,如下所示:

  <provider android:authorities="${applicationId}.yourcontentprovider" android:name=".YourContentProvider" />