我正在开发一组仅在某些品牌中有所区别的应用程序(想想不同的运动队);但是,我遇到了一个问题,我正在为所有特定品牌的应用程序使用一个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类,并在每个特定的应用程序中自定义它。关于如何解决这个问题而不诉诸于此的任何想法?
答案 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
在您的免费项目和付费项目中,在包中创建一个相同的文件,该文件可以是任何内容,但必须相同。
示例:
使用com.android.app.data在您的免费版本中创建一个新包
创建一个名为Authority.java的文件,并在(Authority.java)中放置:
public class Authority {
`public static final String CONTENT_AUTHORITY = "YOUR PROVIDER";`
}
对付费版本重复此操作,请记住保持包名称和类名相同。
现在,在您的合同文件中,在您的库中使用以下内容:
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包对它进行了测试,发现它可以正常工作。