realm-gradle-plugin 2.2.1 と Android Realm Browser

はじめに

この記事は

Android その3 Advent Calendar 2016 - Qiita

の四日目の記事です。


昨日までの話は

annotationProcessor 対応の realm-gradle-plugin 2.2.1

でうまく動かせなかったことをblogに書いた所、Realm のSlack で zakiさんに言及して頂き

解決までサポートしていただきました。大変ありがとうございます。

試した環境は


なにが駄目だったのか

Android 6系の端末で run-as が効かないため Android Realm Browserを導入していた所、

そちらの依存していたRealmが古すぎるため誤動作が起きてビルドができなかった

の記事に乗っていたやつですね。。

変更前

compile 'com.github.dmytrodanylyk.realm-browser:library:0.0.3'

=>

変更後

compile ('com.github.dmytrodanylyk.realm-browser:library:0.0.3'){
        exclude module: 'io.realm'
}

コレでビルド自体は通るようになり、soの duplicateエラー も解決

確かに

realm-browser/build.gradle at master · dmytrodanylyk/realm-browser · GitHub

の指定を見ると

compile 'io.realm:realm-android:0.83.1'

でビルドされています・・*1

gradleの指定で勘違いしていた所

aarで提供されているライブラリの場合、googleのライブラリとかだと

compile 'com.android.support:appcompat-v7:23.1.0'

とか依存が無視されて本体側のが利用されるので

realmとかもそうだと思ってましたorz。

強参照なんですね。。(汗


realm-gradle-plugin 2.2.1 に変更後に、更に修正した箇所

Realm.instanceの取得方法

  • Realm.getDefaultInstance()
  • Realm.setDefaultConfiguration()

が使えるようになったので、そこら辺は記述を戻しました

  • 一旦戻したコード*2
public class RealmUtils{

    public static Realm getInstance(){
        return Realm.getDefaultInstance();
    }

    //Applicationクラス等で初期化する
    public static void setConfiguration(Context context) {
        Realm.setDefaultConfiguration(buildRealmConfiguration(context));
    }

    private static RealmConfiguration buildRealmConfiguration(Context context) {
        return new RealmConfiguration.Builder(context)//★
               .deleteRealmIfMigrationNeeded()
               .schemaVersion(1L)
               .name("testApp") //filename
               .build();
    }
}
  • => 変更後(2.2.1 ver)
public class RealmUtils{

    public static Realm getInstance(){
        return Realm.getDefaultInstance();
    }

    public void setConfiguration(Context context) {
        Realm.setDefaultConfiguration(buildRealmConfiguration(context));
    }

    private RealmConfiguration buildRealmConfiguration(Context context) {
        Realm.init(context); //★
        return new RealmConfiguration.Builder()//★
               .deleteRealmIfMigrationNeeded()
               .schemaVersion(1L)
               .name("testApp") //filename
               .build();
    }
}

データ操作関数(例:削除)

  • 変更前(前回 ver)
RealmResults<User> result = realm.where(User.class).findAll();
result.clear()

でしたが、実際動かしてみた所、動作もしませんでした。*3

  • 変更後(2.2.1 ver)
RealmResults<User> result = realm.where(User.class).findAll();
result.deleteAllFromRealm()

にしたら動くようになりました。

allObjectsSortedがなくなっていた

results = realm.allObjectsSorted(User.class, 
                                 "createAt", true);

=>

results = realm.where(User.class)
               .findAllSorted("createAt", Sort.ASCENDING);

ようはRealm変数に生えていたデータ操作関数が廃止されたってことなんでしょうね・・

  • 参考

blog.ch3cooh.jp


古いコードのバージョンアップ(12/9追記)

  • @PrimaryKey に設定されている変数がObject型だとエラーになる*4

  • 新しいRealmObjectの変数は、全部NULL許可

  • @PrimaryKey付いていたObject変数にもその属性がついてしまう
    • @PrimaryKeyはNotNullが必須なので、スキーマエラーになりgetInstanceがエラーor NULL になる
Realm.init(context);
return new RealmConfiguration.Builder()
            .schemaVersion(REALM_SCHEMA_VERSION) //★要インクリメント
            .migration(new MyMigration())
            .build();
public class MyMigration implements RealmMigration {
    /** Current Schema Version. */
    public static final long REALM_SCHEMA_VERSION = 1;

    @Override
    public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
        Log.d(getClass().getSimpleName(), "Migrate Realm Schema old[" + oldVersion + "] to new[" + newVersion + "]");
        if (oldVersion == 0) {
            // Migrate from v0 to v1
            migrate0To1(realm);
            oldVersion++;
        }

        if (oldVersion < newVersion) {
            throw new IllegalStateException(String.format("Migration missing from v%d to v%d", oldVersion, newVersion));
        }
    }

    /**
     * Migrate from version 0 to version 1.
     * At version 1, add @Required annotation for Primary Key(historyForShow) at SearchHistory Table.
     *
     * @param realm
     */
    private void migrate0To1(DynamicRealm realm) {
        RealmObjectSchema userSchema = realm.getSchema().get(User.class.getSimpleName());
        userSchema.isRequired("key");
    }
}

で使いたかった Android Realm Browser の対応

  • realm-gradle-plugin 2.2.1 に差し替えたままでは動きませんでした

compile 'io.realm:realm-android:0.83.1'

のころの記述のせいか、最新のversionでは動きません

で fork 修正されたもので一番最新に対応されているものを探してみます


だと

realm-browser/build.gradle at master · atsushi-ageet/realm-browser · GitHub

classpath "io.realm:realm-gradle-plugin:1.0.0"

とバージョンは低いですが、realm-pluginを使っていますので、動かせそうです

で導入してみます

  • app/build.gradle
compile ('com.github.atsushi-ageet:realm-browser:0.0.6'){
    exclude group: 'io.realm'
}
  • app/XXXActivity
Realm realm = Realm.getDefaultInstance();
com.dd.realmbrowser.RealmBrowser.getInstance().addRealmConf(realm.getConfiguration());
com.dd.realmbrowser.RealmBrowser.startRealmFilesActivity(activity);

呼び出し方も以前と変わっているようですね。

でもまあ、

startRealmFilesActivity(Activity)

=>

startRealmFilesActivity(Context)

とかだと使い勝手が上がるのにな。。とか思ったりも*5


Realm自体は範囲外の小話的なメモ

新しいバージョンで

  • RealmModelinterfaceで追加しやすい*6
  • publicで書けばsetter/getterいらない*7

という書き方は理解したのですが

@RealmClass
public class UserSimple implements RealmModel {
    @PrimaryKey
    public long id;

    public String name;
    public String email;   
        public int age;
}

public class UserHoge extends UserSimple

と書くと、RealmObjectと同じく継承エラーが出るのは変わらず。。

結局

  • Realm用のDtoを別途作る
  • UserSimpleはそのまま。Gson変換に頼る

みたいなお話になってしまったり。。

でも、対象クラスが implements Parcelable だとGsonがtoJson等でうまく変換できないんですよね・・*8

どっちもG様のプロダクトのはずなんですが何でなんでしょう。。

github.com

辺りしかググっても引っかからないけど、使い方が今一わからないorz

*1:realm-gradle-plugin でも共存指定禁止!!と確かに書いてましたね><

*2:1個前のコードではRealm.getInstance() に修正していた

*3:@eprecatedと言いながら、内部でUnSupportExceptionをthrowして強制エラーにしているw

*4:プリミティブ型だと問題ない

*5:上記の箇所は大本と同じまま。。

*6:extends RealmObject よりも

*7:kotolinライクな書き方がしやすくなるという緩和改修らしい

*8:RealmObject自体をGsonに変換できないのは言わずもがなですが。。