頭のメモリが少ない

プログラミングと日々の生活

Cursor用Bindingライブラリ"Curson"

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

github.com

※本記事は以前qiitaに投稿したものと同様の内容です

qiita.com

ContentProviderのauthorityの取得方法

ContentProviderのauthorityの取得方法

ContentProviderを使用する場合に、authorityはmanifestに書いてあるauthorityをコピペで

public static final String AUTHORITY = "com.hogehoge.jp";

定数として定義するのが一般的なやり方だと思います。

AndroidManifest.xmlのauthorityはContextとContentProviderの実装クラスさえ分かれば、コードからでも取ること可能です。

    /**
     * Get ContentProvider authority from AndroidManifest.xml.
     * 
     * @param context context
     * @param providerClass providerClass
     * @return authority
     */
    public static String getAuthority(Context context, Class< ? extends ContentProvider> providerClass){
        PackageManager manager = context.getApplicationContext().getPackageManager();
        try {
            ProviderInfo providerInfo = manager.getProviderInfo(
                    new ComponentName(context, providerClass), PackageManager.GET_META_DATA);
            return providerInfo.authority;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return "";
    }

他にもAndroidManifest.xmlの内容はPackageManagerから色々と取得出来ます。

■参考

http://developer.android.com/reference/android/content/pm/PackageManager.html

※本記事は以前qiitaに投稿したものと同様の内容です

qiita.com