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

时间:2010-07-22 07:28:16

标签: android android-contentprovider

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

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

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

5 个答案:

答案 0 :(得分:25)

这是一个老问题,但我最近正在考虑做类似的事情。使用Build flavor,它现在非常直接。

在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

答案 1 :(得分:20)

ContentProviders由权威机构识别,因此它必须是唯一的。我不认为有任何伎俩。

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

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

希望这有帮助。

答案 2 :(得分: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. 对付费版本重复此操作,请记住保持包名称和类名相同。

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

    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 - 不确定是否允许用相同的答案回答相同类型的问题。

答案 3 :(得分:4)

你可以!

正如this post中所述(我解释了Firebase如何初始化其库而未给出Application#onCreate()方法的上下文),您可以在清单中使用占位符,如下所示:

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

答案 4 :(得分:1)

以下方法可用于在库中打包ContentProvider并在运行时设置ContentProvider的权限,以便可以将其包含到多个项目中而不会发生ContentProvider Authority冲突。这是有效的,因为真正的权威&#39;来自AndroidManifest ......而不是ContentProvider类。

从基本的ContentProvider实现开始.AUTHORITY,CONTENT_URI和UriMatcher是静态的,但不是&#39; final&#39; ....

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);

然后,覆盖&#39; attachInfo&#39;方法,以便在首次初始化ContentProvider时,将使用从AndroidManifest收集的ProviderInfo调用ContentProvider。这将在进行任何可能的查询之前发生,很可能是在初始Application类设置期间。利用这个机会将AUTHORITY,CONTENT_URI和UriMatcher重置为他们真实的&#39;值,由应用程序使用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"/>

现在将用&#34; com.foo.barapp.content&#34;重写权限。而不是默认值,UriMatcher将更新为应用程序的值而不是默认值。依赖于&#34; AUTHORITY&#34;的课程。现在将访问更新的值,UriMatcher将正确区分&com; foo.barapp.content&#39;的传入查询。

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