Android Content Provider权限定义是否会破坏DRY规则?

时间:2016-04-19 08:39:52

标签: android android-contentprovider dry

Android的Content Provider must have

  

必须至少指定一个权限。

例如,在Google的示例android-BasicSyncAdapter AndroidManifest.xml中有

<provider
    android:name=".provider.FeedProvider"
    android:authorities="com.example.android.basicsyncadapter"
    android:exported="false" />

然后要实现此CP,需要在CP中定义相同的字符串,如android-BasicSyncAdapter FeedProvider.java CONTENT_AUTHORITY

public static final String CONTENT_AUTHORITY = "com.example.android.basicsyncadapter";

由于我们必须两次定义此String,这基本上不会破坏DRY规则 - 如果我在一个地方更改它,我必须记得在其他地方更改它。

1 个答案:

答案 0 :(得分:2)

实际上,您不必多次指定权限。在我们的OpenTasks Provider中,我们在运行时从清单加载权限。

基本理念是:

Context context = getContext();
PackageManager packageManager = context.getPackageManager();
ProviderInfo providerInfo = packageManager.getProviderInfo(
    new ComponentName(context, this.getClass()),
    PackageManager.GET_PROVIDERS | PackageManager.GET_META_DATA);
String authority = providerInfo.authority;

在Android 2.2上,由于方法getProviderInfo尚未出现,您必须付出额外的努力。

您可以选择在String资源中指定权限,并从Manifest中引用它,如下所示:

<provider
    android:name=".provider.FeedProvider"
    android:authorities="@string/authority"
    android:exported="false" />

在您的内容提供商中,您可以照常加载权限:

String authority = getContext().getString(R.string.authority);

如果您的内容提供商为多个权限服务,这会有点困难,但无论如何这可能不是一个好主意。

更新以跟进您的评论:

我没有看到为合同提供Context的问题。

而不是写

之类的东西
Cursor c = contentProvider.query(MyContract.Items.CONTENT_URI,
    null, null, null, null);

你要写

Cursor c = contentProvider.query(MyContract.Items.itemsUri(getContext()),
    null, null, null, null);

public final interface MyContract {

    // ... lots of other tables ...

    public final static class Items {
       public final static String CONTENT_PATH = "items";

       // all the contract definitions

       public final static Uri itemsUri(Context context) {
           return new Uri.Builder()
               .scheme("content")
               .authority(context.getString(R.string.authority)).
               .path(CONTENT_PATH)
               .build();
       }
}

除了在您还需要添加ID时更方便之外,没有什么区别:

Cursor c = contentProvider.query(MyContract.Items.itemUri(getContext(), myItemId),
    null, null, null, null);

Context传递给这些静态方法通常不是问题,因为您无论如何都需要上下文才能获得ContentResolver

这种技术的一个优点是您的代码完全独立于实际权限,您可以轻松地将ContentProvider作为库导入具有不同权限的不同项目中,并且您不需要改变你的代码的一行。

顺便说一下,我更喜欢第一个版本(从Manifest加载权限),因为在这种情况下你甚至不需要String资源。你可以inject ${applicationId}而不再触摸它。

以下清单摘要保证您的权限对于您导入的每个应用都是唯一的:

<provider
    android:name=".provider.FeedProvider"
    android:authorities="${applicationId}.myauthority"
    android:exported="false" />