【问题标题】:Multiple Apps use same content provider多个应用程序使用相同的内容提供者
【发布时间】:2010-07-22 07:28:16
【问题描述】:

我正在开发一组仅在某些品牌中独树一帜的应用(想想不同的运动队);但是,我遇到了一个问题,我为所有特定品牌的应用程序使用一个库项目,并希望对所有这些应用程序使用相同的 ContentProvider。当我创建 ContentProvider 时,我将 AUTHORITY 声明为类中的常量(根据开发示例代码),并且我在清单文件中的每个特定应用程序中使用相同的权限。看起来我无法在每个应用程序中使用相同的权限,因为在尝试安装第二个应用程序时出现此错误(我安装了一个品牌的应用程序很好,但第二次安装):

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

我尝试了几种方法,但似乎都没有奏效。我还没有完成的一个想法是创建一个库 jar 并省略我拥有的 Provider 类并在每个特定的应用程序中对其进行自定义。关于如何在不诉诸于此的情况下解决此问题的任何想法?

【问题讨论】:

标签: android android-contentprovider


【解决方案1】:

这是一个老问题,但我最近正在考虑做类似的事情。有了 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" />

使用 BuildConfigField 变量在提供者中设置权限:

    public static final String AUTHORITY = BuildConfig.AUTHORITY

【讨论】:

  • 这应该是新接受的答案。虽然您可以通过在运行时使用getApplicationContext().getPackageName() 来避免创建buildConfigField
  • 我为此使用了 BuildConfig.APPLICATION_ID - 我在内容提供程序中没有可用的应用上下文。
【解决方案2】:

ContentProviders 是由权限标识的,所以它需要是唯一的。我不认为这有什么技巧。

此外,Android 平台中存在一个错误,该错误还会阻止对两个不同的 ContentProvider 使用相同的类名,即使它们具有不同的权限并且包含在不同的 APK 中。请参阅错误here

我建议您的解决方案是在您的库项目中创建抽象提供程序类,然后在每个单独的应用程序中使用唯一名称对其进行扩展。为了实现这一点,您可能需要创建一个脚本来生成/修改各个清单和 contentprovider 类。

希望这会有所帮助。

【讨论】:

  • 每当您想扩展 android 基本功能集并假设它会起作用(因为它很直观),您就会知道它不起作用。
  • 这个答案不再适用于较新版本的 Android,因为谷歌在 2014 年解决了该问题,如 here 所述
  • 不要创建脚本,使用productFlavors:developer.android.com/studio/build/build-variants
【解决方案3】:

你可以!

正如this post 中所说(这解释了 Firebase 如何在不从您的 Application#onCreate() 方法中提供上下文的情况下初始化其库),您可以在清单中使用占位符,如下所示:

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

【讨论】:

【解决方案4】:

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

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

例子:

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

  2. 创建一个名为 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 - Having Provider authority in the app project 免责声明:我也将其发布在这里:Android duplicate provider authority problem - 不确定是否允许以相同的答案回答相同类型的问题。

【讨论】:

    【解决方案5】:

    可以通过以下方式将一个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 时,将使用从 AndroidManifest 收集的 ProviderInfo 调用您的 ContentProvider。这将在进行任何可能的查询之前发生,最有可能在初始应用程序类设置期间发生。借此机会将 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 实际上是与应用程序类一起实例化的,因此它可以访问所有需要的包信息。 ProviderInfo 对象将包含 AndroidManifest 中提供的信息...包含在最终应用程序中的列表。

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

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

    我同时使用示例应用程序和 androidTest 包对此进行了测试,发现它可以正常工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多