読者です 読者をやめる 読者になる 読者になる

うさがにっき

読書感想文とプログラムのこと書いてきます

Realm migration小ネタ集

Android Realm

概要

小ネタ集
Realmのmigration時、気をつけなければならないこと

概要

プリミティブ型を追加するときには必須項目であることを明示すること

例えばこんな感じでboolean型を追加したいとする

if (oldVersion == 0) {
            RealmObjectSchema pictureInfoSchema = schema.get("PictureInfo");

            pictureInfoSchema.addField("isMarker", boolean.class)
                    .transform((DynamicRealmObject obj) -> {
                        obj.setBoolean("isMarker", false);
                    });
            oldVersion++;
        }

だがこれだと以下のようなエラーを吐きmigrationに失敗する

Caused by: io.realm.exceptions.RealmMigrationNeededException: Field 'isMarker' does support null values in the existing Realm file. Use corresponding boxed type for field 'isMarker' or migrate using io.realm.internal.Table.convertColumnToNotNullable().

プリミティブ型にはnullは入ることがないので必須項目であることを明示する必要がある

if (oldVersion == 0) {
            RealmObjectSchema pictureInfoSchema = schema.get("PictureInfo");

            pictureInfoSchema.addField("isMarker", boolean.class, FieldAttribute.REQUIRED)
                    .transform((DynamicRealmObject obj) -> {
                        obj.setBoolean("isMarker", false);
                    });
            oldVersion++;
        }

RealmConfigurationの設定はApplicationで行うのが良い

サンプルコードを見るとRealmConfigrationをActivityで行っている
realm-java/MigrationExampleActivity.java at master · realm/realm-java · GitHub
抜粋

public class MigrationExampleActivity extends Activity {

    public static final String TAG = MigrationExampleActivity.class.getName();

    private LinearLayout rootLayout = null;
    private Realm realm;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_realm_basic_example);

        rootLayout = ((LinearLayout) findViewById(R.id.container));
        rootLayout.removeAllViews();

        // 3 versions of the databases for testing. Normally you would only have one.
        copyBundledRealmFile(this.getResources().openRawResource(R.raw.default0), "default0");
        copyBundledRealmFile(this.getResources().openRawResource(R.raw.default1), "default1");
        copyBundledRealmFile(this.getResources().openRawResource(R.raw.default2), "default2");

        // When you create a RealmConfiguration you can specify the version of the schema.
        // If the schema does not have that version a RealmMigrationNeededException will be thrown.
        RealmConfiguration config0 = new RealmConfiguration.Builder(this)
                .name("default0")
                .schemaVersion(3)
                .build();
・・・

が、アプリ起動中何回も呼ぶものではないのでApplicationクラスで実装するほうがいい

public class TestApplication extends Application {
    private Tracker mTracker;

    @Override
    public void onCreate() {
        super.onCreate();

        // 従来通りRealmConfigurationを作成
        RealmConfiguration realmConfig = new RealmConfiguration.Builder(getApplicationContext())
                .schemaVersion(1)
                .migration(new Migration())
                .build();

        Realm.setDefaultConfiguration(realmConfig);
    }

Realm.migrateRealm()はもう呼ばなくていい

サンプルコードを見るとcallしているのだが、

Realm.setDefaultConfiguration(realmConfig);

をすればmigrateしてくれるので呼ぶ必要はない
最新版では修正されているが、バージョンによってはこれが起因で落ちたりするので注意
Add a check if database file exists when Realm.migrateRealm() is called. · Issue #2316 · realm/realm-java · GitHub