问题
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