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.
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