2010-07-22 60 views
27

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

+0

你可以尝试申请我的解决方案为类似的任务:http:// stackoverflow。com/a/15964372/1220237 – Sash0k 2013-04-12 06:19:11

回答

17

ContentProviders由权威机构标识,因此它必须是唯一的。我不认为这有什么窍门。

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

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

希望这会有所帮助。

+0

无论何时您想要扩展android基本功能集,并且您认为它会起作用(因为它的直观性),您就会知道它不会。 – havexz 2012-07-13 17:42:12

+1

这个答案不再适用于更新版本的Android,因为Google在2014年解决了这个问题,因为[https://code.google.com/p/android/issues/detail?id=7716#c12] – Keridano 2016-03-23 11:15:40

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

现在你应该可以使用两个部门。

来源:伊恩Warick(代码写了) Android - Having Provider authority in the app project 免责声明:我张贴在这里还有:Android duplicate provider authority problem - 不知道是否允许使用同样的答案回答同一个类型的问题。

1

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

先从基本的ContentProvider implementation..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的实际上是与应用类一起实例化,所以将有机会获得所有所需的程序包信息。 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包,并发现它能正常工作。

9

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

指定BuildConfigField在gradle这个文件:

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 
+0

这应该是新接受的答案。尽管可以避免在运行时使用getApplicationContext()。getPackageName()来创建'buildConfigField'。 – 2017-11-08 01:08:39

1

你可以!

this post说(至极解释了火力地堡怎么没有给它从你的Application#onCreate()方法的情况下初始化其库),你可以在你的清单使用占位符,就像这样:

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

很好的回答!简短,甜美,并重点!谢谢! – Sakiboy 2017-07-13 05:46:28

+0

其实...太短,除非你知道你在做什么,否则不工作。我会建议这个答案:https://stackoverflow.com/a/43444164/2371425 – Sakiboy 2017-07-27 04:30:26