CursorAdapterとContentProviderの関係

AndroidでCursorのデータを一覧表示する場合、CursorAdapterが使用されていました。
このとき、データの更新をViewに反映するということについてフレームワークがなにを提供しているのかがわかりづらかったので、メモとして整理しておきます。

MediaStoreのデータをListViewで表示した場合

ContentProvider経由でCursorを取得し、CursorAdapterで表示するとどうなるか確認します。

そこで今回は、MediaStoreに保存された画像すべてを、ListViewで一覧表示してみます。

(サンプルなので、UIスレッドでクエリを発行しちゃいます)

public class SampleCursorAdapterActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        ListView listView = (ListView) findViewById(R.id.listView1);
        Cursor cursor = getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, null, null, null, null);
        SimpleCursorAdapter mAdapter = new SimpleCursorAdapter(this, 
        		android.R.layout.simple_list_item_2, 
        		cursor, 
        		new String[] {MediaStore.Images.Media.DATA, MediaStore.Images.Media._ID}, 
        		new int[] {android.R.id.text2,android.R.id.text1});
        listView.setAdapter(mAdapter);
        
    }
}

これで、ListViewに画像一覧が表示されます。

このアクティビティが起動したまま、画像をギャラリーに追加されるとどうなるでしょうか。
(カメラアプリで写真を撮るなどして)
実際に写真を撮って、バックグラウンドのアクティビティを再度表示してみると、ListViewにはきちんとレコードが追加され、表示が更新されていることが確認できます。
notifyDatasetChangedや、requeryなどは明示的に行っていないにも関わらずです。

CursorAdapterは関連づけられたUriの変更を検知する

このような動作になるのは、CursorAdapterがCursorに関連づけられたUriの変更を検知できるからです。

CursorとCONTENT_URIの紐付けは、各URIのContentProvider内部で実装されています。
正しく実装されたContentProviderは、クエリの結果としてCursorを返却するときにCursor#setNotificationUriによってCursorにnotificationUri(ギャラリー画像ならMediaStore.Images.Media.EXTERNAL_CONTENT_URI)が設定されています。
これにより、ギャラリーへの画像追加がContentProvider経由で行われると、ContentProvider#notifyChangeでcursorに変更を通知します。onContentChangedで検知し、自身のデータを更新します。

CursorAdapterが変更を検知できるのは、正しく実装されたContentProvider経由でCursorを取得したときだけ

ContentProviderを経由せず直接SQLiteとCursorをやりとりしたり、ContentProviderを経由していてもsetNotificationUriの実装が抜けていたりすると、CursorAdapterのデータ更新機能は動作しません。

HoneyCombからはCursorLoader

サンプルコードではmanagedQueryなど細かい部分には触れていませんが、honeyCombではmanagedQueryが廃止され、CursorLoaderへの移行が促されています。これについては後日あらためて書こうと思います。