Cursor操作をチョットだけ楽にする

  • 5
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

Cursor操作をチョットだけ楽にする

Androidを開発していたら必ずと言っていいほどCursorが出てきますね。
例えば、DBにqueryしてCursorのインスタンスを取得して、そのcursorをcursor.moveToFirstでポジションを移動して、getColumnIndexでindexを取得してからgetString, getInt等で値を取得して、最後にはちゃんとcursorをcloseしてなどなど...。
こんな感じでしょうか。

Cursor cursor = null;
try {
    cursor = getCursor(...);
    if (cursor != null && cursor.moveToFirst()) {
        int columnIndex = cursor.getColumnIndex(MediaStore.Images.Media._ID);
        Long id = cursor.getLong(fieldIndex);
        //以下、略...
    }
} finally {
    if (cursor != null) {
        cursor.close();
    }
}

毎度毎度同じコード書くのは辛いので、このようなボイラーテンプレートなコードは自動生成するに限ります。
という訳で、Cursorを操作をチョットだけ楽にするために、Cursor<->Entity変換を行うライブラリCursonを作りました。

How to install ?

build.gradleに下記を記述してください

buildscript {
  repositories {
    mavenCentral()
   }
  dependencies {
    classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
  }
}

apply plugin: 'com.neenbedankt.android-apt'

dependencies {
    compile 'com.github.dkajiwara:curson:1.0.2'
    apt 'com.github.dkajiwara:curson-compiler:1.0.2'
}

How to use?

使い方は簡単です、Cursorに対応するEntityオブジェクトを作成してください。
次に、作成したEntityオブジェクトとCursorのインスタンスをCursor.fromCursor()を引数に渡してください。
具体的なコードを書いたほうが分かりやすいと思いますので、下記に端末内の画像取得する場合の例を載せています。

public class Image {
    @CursorRow(ImageColumns.BUCKET_DISPLAY_NAME/*カラム名*/)
    String bucketDisplayName;

    @CursorRow(ImageColumns.BUCKET_ID)
    String bucketId;
}

public class ImageManager {
    public List<Image> getImages() {
        Cursor cursor = query...//image取得する
        return Curson.fromCursor(cursor, Image.class);
    }

    public Image getImage() {
        Cursor cursor = query...//image取得する
        return Curson.fromCursor(
                cursor,
                Image.class /*作成したEntityクラスを指定する*/,
                0 /*cursorのpositionを指定する*/);
    }
}

上記の例では、Cursorのcloseを呼んでいません。これはCursor.fromCursor()ではデフォルトでcursor.close()を呼んでいます。なので、変換時にCursorのcloseをしたくない場合は、メソッドCurson#fromCursor(Cursor, Class, int, boolean)を使い、引数(boolean)にfalseを指定してください。
また、CursorRowアノテーションは下記の型をサポートしています。

  • byte[]
  • int / Integer
  • double / Double
  • float / Float
  • short / Short
  • String

Other

Cursorオブジェクトへの変換としてCursor.toCursor()メソッドをサポートしています。EntityオブジェクトからCursorオブジェクトへの変換なのであまり使う機会は少ないかと思いますが、UnitTest時のContentProviderのモックで使用するとチョットだけ楽になります。


public class ExContentProvider extends ContentProvider {
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        List<Sample> list = new ArrayList<>();
        list.add(new Sample(1, 100000000L, "description1", "title1", 1));
        list.add(new Sample(2, 100000001L, "description2", "title2", 1));
        list.add(new Sample(3, 100000002L, "description3", "title3", 0));
        list.add(new Sample(4, 100000003L, "description4", "title4", 1));
        return Curson.toCursor(list, Sample.class);
    }
}

End

今回、ライブラリの使い方のみで中身の実装に関しては触れていませんが、雑に説明するとコンパイル時にアノテーションプロセッシングを使用してEntityクラスから下記のようなクラスを生成しています。実行時には内部でこのクラスを呼び出すことでCursor<->Entityオブジェクトの変換を行う仕組みとなっています。

public class Sample$$CursonEntityBinder implements CursorBinder<Sample> {
  @Override
  public Sample bind(Cursor cursor) {
    final int idIndex = cursor.getColumnIndex("_id");

    Sample sample = new Sample();
    sample.id = cursor.getInt(idIndex);

    return sample;
  }
  //いろいろ省略...
}

Reference

Curson
https://github.com/dkajiwara/Curson