2012年11月5日月曜日

Android - ContentProviderの更新通知の仕組み

コンテンツプロバイダのqueryメソッド内のCursor.setNotificationUri()、及びinsert, update, deleteメソッド内のContentResolver.nofityChange()の使い方がイマイチよく分からなかったので調査。
public final class MainActivity extends FragmentActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

// まずデータ初期化. 3件入れておく.
getContentResolver().delete(Contract.TABLE1.contentUri, null, null);
ContentValues values = new ContentValues();
for (int i = 0; i < 3; i++) {
values.clear();
values.put(Contract.TABLE1.columns.get(1), "title" + i);
values.put(Contract.TABLE1.columns.get(2), "note" + i);
getContentResolver().insert(Contract.TABLE1.contentUri, values);
}

// table1テーブルのデータを全て変更.
findViewById(R.id.update).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ContentValues values = new ContentValues();
values.put(Contract.TABLE1.columns.get(1), "modified");
values.put(Contract.TABLE1.columns.get(2), "modified");
final int updatedCount = getContentResolver().update(Contract.TABLE1.contentUri, values, null, null);
Log.d(getClass().getSimpleName(), "updated. count: " + updatedCount);
}
});

// table1テーブルのデータを全件検索. 表示.
final Cursor c = getContentResolver().query(Contract.TABLE1.contentUri, null, null, null, null);

// Cursorに関係するデータが更新された事を通知する.
c.registerContentObserver(new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
Log.d(getClass().getSimpleName(), "onChange called. selfChange: " + selfChange);
c.requery();
}
});

// 既に検索済みのcursorを使ってデータをログ表示する.
findViewById(R.id.query).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
c.moveToFirst();
do {
for (int i = 0; i < c.getColumnCount(); i++) {
Log.d(getClass().getSimpleName(), c.getColumnName(i) + " : " + c.getString(i));
}
} while (c.moveToNext());
}
});
}
}
これをQUERY→UPDATE→QUERY、の順で実行してみた。以下がそのログ。
05-18 11:33:34.108: D/(5583): _id : 34
05-18 11:33:34.108: D/(5583): title : title0
05-18 11:33:34.108: D/(5583): note : note0
05-18 11:33:34.108: D/(5583): _id : 35
05-18 11:33:34.108: D/(5583): title : title1
05-18 11:33:34.108: D/(5583): note : note1
05-18 11:33:34.108: D/(5583): _id : 36
05-18 11:33:34.108: D/(5583): title : title2
05-18 11:33:34.108: D/(5583): note : note2
05-18 11:33:37.121: D/(5583): updated. count: 3
05-18 11:33:37.121: D/(5583): onChange called. selfChange: false
05-18 11:33:40.344: D/(5583): _id : 34
05-18 11:33:40.344: D/(5583): title : modified
05-18 11:33:40.344: D/(5583): note : modified
05-18 11:33:40.344: D/(5583): _id : 35
05-18 11:33:40.344: D/(5583): title : modified
05-18 11:33:40.344: D/(5583): note : modified
05-18 11:33:40.344: D/(5583): _id : 36
05-18 11:33:40.344: D/(5583): title : modified
05-18 11:33:40.344: D/(5583): note : modified
つまり
コンテンツプロバイダのquery()で戻ってきたcursorに対して、Cursor.registerContentObserver()でリスナを登録しておけば、insert, update, deleteのタイミングでコールバックを実行する事が出来るようになる。 上記実験では、cursor.requery()を行なっている為、再度コンテンツプロバイダに問い合わせなくても最新の情報がログ表示されているのが分かる。

ポイントは、予めコンテンツプロバイダのquery()内でsetNotificationUri()を行なっている事のようだ。これをコメントアウトしたらコールバックは呼ばれなかった。 恐らく、対象のURIでnotifyChange()されたら、その通知をコールバックに送る、という事だと思われる。

URIが完全一致でなくても大丈夫
content://authority/table/ をquery()した後content://authority/table/_id をupdate()
content://authority/table/_id をquery()した後content://authority/table/ をupdate()
上記の場合でもコールバックは呼ばれた。どうも、CONTENT_URIの部分が一致していさえすれば良いようだ。基本的にupdate, deleteは_id指定で実行されるのに対し、queryは複数で実行される場合が多いと思うので、 その場合使えないのであれば微妙だと思ったのだが、そんな事はなかったようで安心。

0 件のコメント:

コメントを投稿