Content Providers
Overview
Content Provider は以下の特徴を持つ。
- 異なるアプリケーションに対して、適切なパーミッションを持つデータアクセスを提供できる。
ContentResolver
を介した URI 形式でのメッセージ通信により、非同期にデータアクセスを行なう。- 各アプリケーションにデータの更新を通知できる。
Content URI
データアクセスは URI のメッセージ通信で行なう。URI から、どの Provider 宛のものかを特定するために、Authority と呼ばれる名前空間を定義する。慣習的に各アプリケーションのパッケージ名に続ける。
AndroidManifest.xml
に <provider>
として登録しておく。
<application ...>
<provider android:name=".TodoProvider"
android:authorities="net.example.android.provider.TodoProvider" />
</application>
Provider は content://(authority)
の後に続けて、各 URI を定義する。クライアントは ContentResolver
を介して、 URI を送信してデータアクセスを行なう。直接 Provider にアクセスすることはできない。
Uri uri = Uri.parse("content://net.example.android.provider.TodoProvider/tasks");
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
ContentUris
パス名の最後尾に Long 型の ID を指定する URI の場合、Long / String 型に相互に変換する定型処理がある。このために ContentUris
というユーティリティクラスが提供されている。
final Uri CONTENT_URI = Uri.parse("content://net.example.android.provider.TodoProvider/tasks");
long id = 1234;
Uri newUri = ContentUris.withAppendId(uri, id);
assert "content://net.example.android.provider.TodoProvider/tasks/1234".equals(newUri.toString());
assert id == ContentUris.parseId(newUri);
ContentProvider
独自の Content Provider を作るには ContentProvider
を継承する。
public class TodoProvider extends ContentProvider {
@Override
public boolean onCreate() {
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
Cursor cursor;
...
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues contentValues) {
long id;
...
if (id > 0) {
Uri newUri = ContentUris.withAppendedId(uri, id);
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
} else {
throw new SQLException("Failed to insert " + contentValues);
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int n; // the number of rows affected
...
getContext().getContentResolver().notifyChange(uri, null);
return n;
}
@Override
public int update(Uri uri, ContentValues contentValues,
String selection, String[] selectionArgs) {
int n; // the number of rows affected
long id = ContentUris.parseId(uri);
...
getContext().getContentResolver().notifyChange(uri, null);
return n;
}
}
query
ではLoader
を介してデータを監視しているクライアントのために、Cursor#setNotificationUri
で通知URIをセットしておく。(insert|update|delete)
ではLoader
を介してデータを監視しているクライアントのために、ContentResolver#notifyChange
で変更を通知する。
UriMatcher
URI のパターンマッチのために、UriMatcher
が提供されている。
private static final UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int URI_TASKS = 1;
private static final int URI_TASKS_ID = 2;
private static final int URI_TASK_LABELS = 3;
private static final int URI_TASK_LABELS_NAME = 4;
static {
// content://(authority)/tasks
sUriMatcher.addURI("tasks", URI_TASKS);
// content://(authority)/tasks/123
sUriMatcher.addURI("tasks/#", URI_TASKS_ID);
// content://(authority)/tasks/123/labels
sUriMatcher.addURI("tasks/#/labels", URI_TASK_LABELS);
// content://(authority)/tasks/123/labels/foo
sUriMatcher.addURI("tasks/#/labels/*", URI_TASK_LABELS_NAME);
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
switch (sUriMatcher.match(uri)) {
case URI_TASKS:
...
break;
...
default:
...
}
}
...