Android: SQLiteOpenHelperコンストラクタの引数CursorFactoryについて

### Overview

SQLiteOpenHelperのコンストラクタを見てみると第三引数にCursorFactoryを受け取るようになっている. 

```java
SQLiteOpenHelper (Context, String, CursorFactory, int)
```

SQLiteデータベースを使用したサンプルコードではnullが指定されるケースが多く, あまり活用事例を見ない. 

```java
db = new MyDatabase(context, DB_NAME, null /*CursorFactory*/, DB_VERSION);
```

本稿ではCursorFactoryの利用する方法について記載する. 

引用元: [Yukiの枝折 - SQLiteOpenHelperコンストラクタの引数CursorFactoryについて](http://yuki312.blogspot.jp/2014/11/android-sqliteopenhelpercursorfactory.html)

SQLiteDatabaseにクエリを発行するとCursorが返却される. 
Android標準では, このCursor生成処理を拡張できるCursorFactoryクラスを用意している. 
CursorFactoryを使ってCursorクラスを得るまでの一般的な流れは下記.

![cursorFactory.png](https://qiita-image-store.s3.amazonaws.com/0/3888/8a55c75d-22a6-5084-7ca6-99a2da6d5362.png)

CursorFactoryを実装することで次のことが実現できる. 

 - Cursor生成のタイミングをフックする
 - クエリ結果として独自Cursorを返す

ただし, 2点目の独自Cursorを返すについては注意事項がある(後述). 


### API Reference.

CursorFactoryと周辺のクラスを次に記す. 

**SQLiteOpenHelper** - [API Reference - Android Developers](http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html)
SQLiteDatabaseの生成とバージョンを管理するクラス. 

**SQLiteDatabase** - [API Reference - Android Developers](http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html)
SQLiteデータベースの生成・破棄・問合せといったSQLの実行や管理を行うクラス. 

**SQLiteDatabase.CursorFactory** - [API Reference - Android Developers](http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.CursorFactory.html)
SQLiteDatabaseへのクエリ結果を格納したCursorのファクトリクラス. ここで生成されるCursorインスタンスがクエリの戻り値(Cursor)として使われる. 

**SQLiteCursor** - [API Reference - Android Developers](http://developer.android.com/reference/android/database/sqlite/SQLiteCursor.html)
SQLiteへのクエリ結果を格納したCursorの具象クラス. 内部のSQLiteCursorを直接操作する場合は別途スレッドセーフを自前で担保する必要がある. 

**SQLiteQueryBuilder** - [API Reference - Android Developers](http://developer.android.com/reference/android/database/sqlite/SQLiteQueryBuilder.html)
SQLiteDatabaseへのクエリ構築を手助けするビルダクラス. 


### Set CursorFactory

Android標準でCursorの生成は[SQLiteDirectCursorDriver.query](http://tools.oesf.biz/android-5.0.0_r2.0/xref/frameworks/base/core/java/android/database/sqlite/SQLiteDirectCursorDriver.java#43)で行われる. 

```java
public Cursor query(CursorFactory factory, String[] selectionArgs) {
    ...
        if (factory == null) {
            cursor = new SQLiteCursor(this, mEditTable, query);
        } else {
            cursor = factory.newCursor(mDatabase, this, mEditTable, query);
        }
    ...
}
```

CursorFactoryが指定されていればCursorFactory.newCursorメソッドを経由してCursorを生成する. 
ここで独自のCursorFactoryが指定されていればCursorの生成をフックし, 独自Cursorを返すことができる. 

#### implements

CursorFactoryはSQLiteDatabaseの取得時に指定する.
SQLiteDatabaseにCursorFactoryを設定するメソッドはいくつかある. 

 - [SQLiteDatabase.create](http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#create(android.database.sqlite.SQLiteDatabase.CursorFactory))
 - [SQLiteDatabase.openDatabase](http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#openDatabase(java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int))

しかし, 一般的にSQLiteDatabaseのメソッドを直接操作することはせず, SQLiteOpenHelperやSQLiteQueryBuilderを経由する. 
そのため, SQLiteOpenHelperやSQLiteQueryBuilderにもCursorFactoryを設定するためのAPIが用意されている. 

 - [SQLiteDatabase Constructor](http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#SQLiteOpenHelper(android.content.Context, java.lang.String, android.database.sqlite.SQLiteDatabase.CursorFactory, int))

```java
public class MyDBHelper extends SQLiteOpenHelper{
    public MyDBHelper(Context context){
        super(context, DB_NAME, CURSOR_FACTORY, DB_VERSION);
    }
    ...
}
```

 - [SQLiteQueryBuilder.setCursorFactory](http://developer.android.com/reference/android/database/sqlite/SQLiteQueryBuilder.html#setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory))

```java
@Override
public Cursor query(Uri uri, String[] projection, String selection,
                    String[] selectionArgs, String sortOrder) {
    SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
    builder.setCursorFactory(CURSOR_FACTORY);
    ...
}
```

なお, CursorFactoryが指定されない場合はCursorのサブクラスであるSQLiteCursorが生成される. 

次に, 独自のCursorFactoryを用意する. 
CursorFactoryにはカーソルを生成するnewCursorメソッドが用意されており, ここでCursorを生成する. 

```java
public class MyCursorFactory implements CursorFactory {
    @Override
    public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
            String editTable, SQLiteQuery query) {
        return new SQLiteCursor(masterQuery, editTable, query);
    }
}
```

独自CursorFactoryを設定するためにSQLiteOpenHelperのコンストラクタ引数にこれを指定する. 
他にもクエリ発行時にCursorFactoryを指定することもできる(後述). 

```java
public class MyContentProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        db = new MySQLiteOpenHelper(context, DB_NAME, 
                new MyCursorFactory(), DB_VERSION);
        ...
    }
}
```

### Query API with CursorFactory

SQLiteDatabaseにクエリを問い合わせるために次のメソッドが用意されている. 

 - [SQLiteDatabase.query (table,  columns, selection, selectionArgs, ...)](http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#query(java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String))
 - [SQLiteDatabase.queryWithFactory(cursorFactory, distinct, table, columns, selection, ...)](http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#queryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, boolean, java.lang.String, java.lang.String[], java.lang.String, java.lang.String[], java.lang.String, java.lang.String, java.lang.String, java.lang.String, android.os.CancellationSignal))
 - [SQLiteDatabase.rawQuery(sql, selectionArgs)](http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#rawQuery(java.lang.String, java.lang.String[], android.os.CancellationSignal))
 - [SQLiteDatabase.rawQueryWithFactory(cursorFactory, sql, selectionArgs, editTable)](http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#rawQueryWithFactory(android.database.sqlite.SQLiteDatabase.CursorFactory, java.lang.String, java.lang.String[], java.lang.String))

queryWithFactory/rawQueryWithFactoryメソッドはCursorFactoryを直接指定してクエリを実行する. 
一方, query/rawQueryメソッドは引数cursorFactoryにnullを指定して最終的にrawQueryWithFactoryを呼ぶ. 

![rawQuery.png](https://qiita-image-store.s3.amazonaws.com/0/3888/33980748-240b-74ef-1ad7-4ffc15f01300.png)

rawQueryWithFactoryメソッドは引数cursorFactoryにnullが指定されるとSQLiteDatabaseに設定されているCursorFactoryを使用する. この挙動にあたる部分のソースコードは下記. 

```java
// rawQuery系メソッドでは引数にnullを指定すると設定した独自CursorFactoryが使用される.
public Cursor rawQueryWithFactory(
        CursorFactory cursorFactory, String sql, String[] selectionArgs,
        String editTable, CancellationSignal cancellationSignal) {
    acquireReference();
    try {
        SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(
                this, sql, editTable, cancellationSignal);
        return driver.query(
                cursorFactory != null ? cursorFactory : mCursorFactory,
                selectionArgs);
    } finally {
        releaseReference();
    }
}
```

> **MEMO**
> SQLiteDatabase.queryメソッドは簡易な検索に向いている. 
> 複雑なクエリを組み立てるにはデータバインドにも対応しているSQLiteDatabase.rawQueryメソッドが便利. 

SQLiteDatabaseにクエリを発行するにはいくつか方法がある. 下記はその一例. 

 1. SQLiteDatabaseインスタンスに直接クエリを投げる
 2. SQLiteQueryBuilder経由でクエリを投げる

#### SQLiteDatabase

前述の通りqueryWithFactory/rawQueryWithFactoryメソッドの引数にCursorFactoryを指定できる. 
nullを指定した場合はSQLiteDatabaseに設定したCursorFactoryが使用される. 

#### SQLiteQueryBuilder

結果的にSQLiteDatabaseのrawQueryWithFactoryが実行される. 
SQLiteQueryBuilderにはCursorFactoryを指定するメソッドが用意されている. 

 - [SQLiteQueryBuilder.setCursorFactory(factory)](http://developer.android.com/reference/android/database/sqlite/SQLiteQueryBuilder.html#setCursorFactory(android.database.sqlite.SQLiteDatabase.CursorFactory))

### Attention

SQLiteDatabaseを使ったアプリケーションではContentProviderも使われることが多い. 
CursorFactoryとContentProviderを併用する場合に注意すべき点がある. 

ContentProviderサイドで生成されたCursorは直接クライアントサイドには届かない. 
SQLiteDatabaseで生成されるCursorはWrapやコピーを経てクライアントサイドに届く. 
つまり, CursorFactoryで独自Cursorを返却しても, それを独自Cursorとして使用(Cast)できるのはそれを生成したContentProviderの処理内に限定される. 
クライアントサイドで独自Cursorにキャストし直すことはできない. 

### Usage

CursorFactoryは使いどころの難しいAPIではあるが, たとえばCursor生成をフックして次のようにクエリをロギングする仕組みは楽に作ることができる. 

```java
public class MyCursorFactory implements CursorFactory {
    @Override
    public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
            String editTable, SQLiteQuery query) {
        if (debug) {
            Log.d(TAG, query.toString());
        }
        return new SQLiteCursor(masterQuery, editTable, query);
    }
}
```

以上.