重いクエリを非同期で行うには

# AsyncQueryHandlerとは？
* 重いクエリをUIスレッド上でするとブロックされる場合があるため、非同期でクエリを実行するときに使用するクラス
* AsyncQueryHandlerでのクエリ実行は、AsyncQueryHandlerに内包されているHandlerThread上で行われます。
 
## 動機
* 参考するページが少ない
* 使おうと思ったら、ちょっと詰まった。
    * 非同期処理の値を返すタイミングがずれてうまくデータがinsertされない事案が発生。

## 主なメソッド
|メソッド名|概要|
|:--|:--|
|startDelete(int token, Object cookie, Uri uri, String selection, String[] selectionArgs)|非同期でdeleteを行う。|
|startInsert(int token, Object cookie, Uri uri, ContentValues initialValues)|非同期でinsertを行う。
|startQuery(int token, Object cookie, Uri uri, String[] projection, String selection, String[] selectionArgs, String orderBy)	|非同期でクエリを行う。
|startUpdate(int token, Object cookie, Uri uri, ContentValues values, String selection, String[] selectionArgs)	|非同期でupdateを行う。
|onDeleteComplete(int token, Object cookie, int result)	|startDeleteメソッドが完了した時に呼び出される
|onInsertComplete(int token, Object cookie, Uri uri)	|startInsertメソッドが完了した時に呼び出される
|onQueryComplete(int token, Object cookie, Cursor cursor)|startQueryメソッドが完了した時に呼び出される
|onUpdateComplete(int token, Object cookie, int result)|startUpdateメソッドが完了した時に呼び出される

第1引数のtokenは処理を識別するために使用する値。
第2引数のcookieは完了時に渡すオブジェクト
完了時に呼ばれるメソッドの第3引数は各々対応するメソッドの戻り値

## 前提
* DB作成をContentProviderなどで行なっておく。

## プログラム
### AsyncQueryHandlerを継承したクラス
```java
class CustomAsyncQueryHandler extends AsyncQueryHandler {

    private static final String[] PROJECTION = new String[]{UserColumns.ID, UserColumns.NAME, UserColumns.EMAIL};

    private final AsyncQueryHandlerActivity activity;

    CustomAsyncQueryHandler(ContentResolver cr, AsyncQueryHandlerActivity activity) {
        super(cr);
        this.activity = activity;
    }

    @Override
    protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
        super.onQueryComplete(token, cookie, cursor);
        SimpleCursorAdapter adapter = new SimpleCursorAdapter(
                activity,
                android.R.layout.simple_list_item_2,
                cursor,
                PROJECTION,
                new int[]{android.R.id.text1, android.R.id.text2});

        activity.setListAdapter(adapter);
    }

    void insert(String name, String address) {
        ContentValues value = new ContentValues();
        value.put(UserColumns.NAME, name);
        value.put(UserColumns.EMAIL, address);
        startInsert(0, null, UserColumns.CONTENT_URI, value);
    }

    void delete() {
        startDelete(0, null, UserColumns.CONTENT_URI, null, null);
    }

    void findAll() {
        startQuery(0, null, UserColumns.CONTENT_URI, PROJECTION, null, null, null);
    }

    void findById(String where, String[] value) {
        startQuery(0, null, UserColumns.CONTENT_URI, PROJECTION, where, value, null);
    }
}
```

### Activity
```java
public class HogeHogeActivity extends ListActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        CustomAsyncQueryHandler asyncQueryHandler = new CustomAsyncQueryHandler(getContentResolver(), this);

        // Delete
        asyncQueryHandler.delete();

        // Insert
        for (int i = 1; i <= 50; i++) {
            asyncQueryHandler.insert(UserColumns.NAME + i, UserColumns.EMAIL + i);
        }

        // Select
        asyncQueryHandler.findAll();

    }

}
```

おそらくもっといい方法があると思いますが、これがそのときの解決法でした。

## 参考
https://developer.android.com/reference/android/content/AsyncQueryHandler.html
http://shop.oreilly.com/product/0636920029397.do