【问题标题】:Does Android Content Provider authority definition break the DRY rule?Android Content Provider 权限定义是否违反 DRY 规则?
【发布时间】:2016-04-19 08:39:51
【问题描述】:

安卓的Content Providermust 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";

由于我们必须定义这个字符串两次,这是否基本上违反了 DRY 规则 - 如果我在一个地方更改它,我必须记住在其他地方也更改它。

【问题讨论】:

  • 嗯,我希望我有你的问题... ;-)

标签: android android-contentprovider dry


【解决方案1】:

实际上您不必多次指定权限。在我们的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 尚不存在。

您也可以选择在字符串资源中指定权限并从清单中引用它,如下所示:

<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 作为库导入到具有不同权限的不同项目中,而无需更改一行你的代码。

顺便说一句,我更喜欢第一个版本(从清单加载权限),因为在这种情况下您甚至不需要字符串资源。你可以只inject ${applicationId} 并且永远不要再碰它。

以下清单 sn-p 保证您的权限对于您导入的每个应用程序都是唯一的:

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

【讨论】:

  • 字符串资源解决方案存在一个问题 - 您必须使用 getContext() - 在扩展 Content Provider 的类中这很容易,但如果您必须在例如 Contract 类中知道此权限值,那么您就是使其依赖于上下文,这是弱的
  • 更新了我的答案以详细说明您的担忧。
  • 看起来不错,我会试试看 - 但为什么 Contract 是一个接口 - 有什么好的理由吗?
  • 你可以把它变成一个类。没有具体原因。我只是认为它更合适,因为 Contract 基本上是某种其他形式的接口(即使您从未将其实例化)。
猜你喜欢
  • 2014-02-15
  • 2010-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-08
相关资源
最近更新 更多