Does Android Content Provider authority definition break the DRY rule?

夙愿已清 提交于 2020-01-01 17:13:38

问题


Android's Content Provider must have:

At least one authority must be specified.

So for example in Google's samples android-BasicSyncAdapter AndroidManifest.xml there is

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

Then to implement this CP one need to define the same String inside the CP like in android-BasicSyncAdapter FeedProvider.java CONTENT_AUTHORITY

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

As we have to define this String twice, isn't this basically breaking the DRY rule - if I change it in one place, I have to remember to change it somewhere else as well.


回答1:


Actually you don't have to specify the authority multiple times. In our OpenTasks Provider we load the authority at runtime from the manifest.

The basic idea is this:

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;

On Android 2.2 you have to take some extra efforts since the method getProviderInfo is not present yet.

Optionally you could just specify the authority in a String resource and refer to it from the Manifest like so:

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

In your content provider you load the authority as usual:

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

It's a bit more difficult if your content provider serves multiple authorities, but that's probably not a good idea anyway.

Update to follow up with your comment:

I don't see a problem with providing a Context to the contract.

Instead of writing something like

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

You would just write

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

with

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

There is not much of a difference, except that it can be even more convenient when you also need to add an ID:

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

Passing a Context to these static methods is usually not a problem since you need the Context anyway to get a ContentResolver.

An advantage of this technique is that your code is completely independent of the actual authority and you can easily import the ContentProvider as a library into a different project with a different authority and you don't need to change a single line of your code.

Btw I prefer the first version (loading the authority from the Manifest) since you don't even need a String resource in that case. You can just inject ${applicationId} and never touch it again.

The following manifest snippet guarantees that your authority is unique for each app that you import it in:

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


来源:https://stackoverflow.com/questions/36713033/does-android-content-provider-authority-definition-break-the-dry-rule

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!