40

我正在开发一组仅在某些品牌中脱颖而出的应用程序(想想不同的运动队);但是,我遇到了一个问题,我为所有特定品牌的应用程序使用一个库项目,并希望对所有这些应用程序使用相同的 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 类并在每个特定的应用程序中对其进行自定义。关于如何在不诉诸于此的情况下解决此问题的任何想法?

4

5 回答 5

26

这是一个老问题,但我最近正在考虑做类似的事情。有了 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
于 2016-08-20T22:32:28.770 回答
22

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

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

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

希望这可以帮助。

于 2011-05-27T21:06:09.900 回答
6

你可以!

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

    <provider
         android:authorities="${applicationId}.yourcontentprovider"
         android:name=".YourContentProvider" />
于 2017-01-31T18:11:13.317 回答
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 - 在应用程序项目中拥有提供者权限 免责声明:我也在这里发布:Android 重复提供者权限问题- 不确定是否允许用相同的答案回答相同类型的问题。

于 2014-01-12T00:41:42.300 回答
1

可以通过以下方式将一个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 包对此进行了测试,发现它可以正常工作。

于 2016-08-09T18:41:43.200 回答