

Androidアプリの開発において、Realmまわりでクラッシュが発生しております。

Realmファイルは下記の時系列で生成、変更を行いました。

  * α版 
    * デフォルトRealmのみを使用
  * β版 
    * RealmModuleを使用して、デフォルトRealmと別のRealmの2つに分割
    * デフォルトRealm側のとあるモデルクラスにて、一部のプロパティを "Date" から "String" へ変更

β版のテスト時ではクラッシュが無いことを確認したため、そのままβ版を製品版としてリリース致しました。  
（α版については内部テストのみに使用したものであり、リリースはしておりません）

## 発生した事象

  * リリース直後から、下記のエラーログが出力される多数のクラッシュを検知（クラッシュ率は全体のダウンロード数の約16%）
  * Google Playから製品版をテスト用端末へインストールしたが、クラッシュが発生しないことを確認
  * α版がインストールされている端末に製品版をアップデートしたところ、下記と同様のエラーログが出力されクラッシュが発生することを確認

* * *

## 当該エラーログ

[code]

    Fatal Exception: java.lang.RuntimeException: Unable to start activity ComponentInfo{xxx}: io.realm.exceptions.RealmMigrationNeededException: Invalid type 'String' for field 'notifyAt' in existing Realm file.
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2482)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2545)
       at android.app.ActivityThread.access$900(ActivityThread.java:186)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1410)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:148)
       at android.app.ActivityThread.main(ActivityThread.java:5636)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:730)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)
    Caused by io.realm.exceptions.RealmMigrationNeededException: Invalid type 'String' for field 'notifyAt' in existing Realm file.
       at io.realm.ChildNotificationEntityRealmProxy.validateTable(ChildNotificationEntityRealmProxy.java:270)
       at io.realm.DefaultModuleMediator.validateTable(DefaultModuleMediator.java:83)
       at io.realm.Realm.initializeRealm(Realm.java:342)
       at io.realm.Realm.createAndValidate(Realm.java:299)
       at io.realm.Realm.createInstance(Realm.java:278)
       at io.realm.RealmCache.createRealmOrGetFromCache(RealmCache.java:143)
       at io.realm.Realm.getDefaultInstance(Realm.java:209)
       at (appname).models.ChildModel.findById(ChildModel.java:56)
       at (appname).models.UserModel.getCurrentChild(UserModel.java:30)
       at (appname).activities.LaunchActivity.sendLaunchScreen(LaunchActivity.java:70)
       at (appname).activities.LaunchActivity.onCreate(LaunchActivity.java:39)
       at android.app.Activity.performCreate(Activity.java:6315)
       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1111)
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2435)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2545)
       at android.app.ActivityThread.access$900(ActivityThread.java:186)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1410)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:148)
       at android.app.ActivityThread.main(ActivityThread.java:5636)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:730)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)
    
[/code]

## 備考

  * テスト用の実機端末 
    * Xperia Z5, Android 6.0
    * Nexus 5, Android 6.0.1
    * Galaxy S3α, Android 4.3
  * クラッシュしている端末、OS情報（Crashlyticsからの情報） 
    * Sony端末が60%、その他が40%
    * OS情報 
      * 6.0が50%
      * 4.0, 5.0が20%ずつ
      * 7.0が10%

お手数ですが、ご教示お願い致します。

* * *

2017/02/23  
追加情報

# マイグレーション処理

[code]

    import android.util.Log;
    import io.realm.DynamicRealm;
    import io.realm.RealmMigration;
    import io.realm.RealmSchema;
    
    public class Migration implements RealmMigration {
        private static final String TAG = Migration.class.getName();
    
        @Override
        public void migrate(final DynamicRealm realm, long oldVersion, long newVersion) {
            Log.d(TAG, "realm migration version:" + String.valueOf(oldVersion));
            RealmSchema schema = realm.getSchema();
        }
    }
    
[/code]

## Realm設定部分

[code]

    private static RealmConfiguration mDefaultConfig;
    private static RealmConfiguration mReadOnlyConfig;
    
    @RealmModule(classes = {
            ChildArticleEntity.class,
            ChildEntity.class,
            ChildNotificationEntity.class,
            FavoriteArticleEntity.class,
            ReadArticleEntity.class
    })
    public static class DefaultModule {
    }
    
    @RealmModule(classes = {
            ArticleEntity.class,
            CategoryEntity.class,
            DateEventEntity.class,
            DaysOldEventEntity.class,
            DaysOldNotificationEntity.class,
            MonthsOldEventEntity.class,
            MonthsOldNotificationEntity.class,
            SuggestSearchWordEntity.class,
            TimelineEntity.class
    })
    public static class ReadOnlyModule {
    }
    
    public static void setupDefaultRealm() {
        if (mDefaultConfig == null) {
            mDefaultConfig = new RealmConfiguration.Builder()
                    .schemaVersion(CURRENT_SCHEME_VERSION)
                    .migration(new Migration())
                    .modules(new DefaultModule())
                    .build();
        }
        Realm.setDefaultConfiguration(mDefaultConfig);
    }
    
    public static Realm getReadonlyInstance() {
        if (mReadOnlyConfig == null) {
            mReadOnlyConfig = new RealmConfiguration.Builder()
                    .name(READ_ONLY_FILE_NAME)
                    .schemaVersion(CURRENT_SCHEME_VERSION)
                    .migration(new Migration())
                    .modules(new ReadOnlyModule())
                    .build();
        }
        return Realm.getInstance(mReadOnlyConfig);
    }
    
[/code]

## スキーマ

[code]

    import java.util.Date;
    
    import io.realm.RealmObject;
    
    public class ChildNotificationEntity extends RealmObject {
        private int childId;
        private String message;
        private String notifyAt;
    
        public int getChildId() {
            return childId;
        }
    
        public void setChildId(int childId) {
            this.childId = childId;
        }
    
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    
        public String getNotifyAt() {
            return notifyAt;
        }
    
        public void setNotifyAt(String notifyAt) {
            this.notifyAt = notifyAt;
        }
    }
    
[/code]

## あらかじめ作成したrealmファイルをアプリケーションに読み込む処理

[code]

    public static void importReadonlyRealmIfNeeded(Context context) {
        try {
            //端末内保持バージョン
            SharedPreferences s = context.getSharedPreferences(AppConst.PACKAGE_NAME, Context.MODE_PRIVATE);
            int versionCode = s.getInt(KEY_VERSION_CODE, 0);
    
            //現在のビルドバージョン
            PackageInfo pInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            int currentVersionCode = pInfo.versionCode;
    
            //version codeがアップデートされるとインポートが走る
            if (versionCode == currentVersionCode) {
                Log.i("realm", "import skip");
                return;
            }
    
            Log.d(TAG, "Copy readonly.realm to this device");
            copyBundledRealmFile(context.getResources().openRawResource(R.raw.readonly), READ_ONLY_FILE_NAME, context);
    
            s.edit().putInt(KEY_VERSION_CODE, currentVersionCode).apply();
            Log.i("realm", "success import");
    
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    private static void copyBundledRealmFile(InputStream inputStream, String outFileName, Context context) {
        try {
            File file = new File(context.getFilesDir(), outFileName);
            FileOutputStream outputStream = new FileOutputStream(file);
            byte[] buf = new byte[1024];
            int bytesRead;
            while ((bytesRead = inputStream.read(buf)) > 0) {
                outputStream.write(buf, 0, bytesRead);
            }
            outputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
[/code]


CC BY-SA 3.0
2017-02-23T05:04:33.003
32891
2018-06-06T13:47:48.110
2020-06-17T08:14:45.997
-1
20786
question
android realm
Realmスキーマ変更に伴うクラッシュについて


マイグレーションの処理が何も記述されていないように見えます。

<https://github.com/realm/realm-
java/blob/v2.3.1/examples/migrationExample/src/main/java/io/realm/examples/realmmigrationexample/model/Migration.java#L114>

に、フィールドの型をStringからintに変更する例があるので、これを参考に `notifyAt` を `Date` から `String`
へ移行する処理を記述してください。


CC BY-SA 3.0
2017-02-23T20:38:36.527
32909
2017-02-23T20:38:36.527
319
32891
answer