做Realm Migration Android的正确方法
Posted
技术标签:
【中文标题】做Realm Migration Android的正确方法【英文标题】:Right way of doing Realm Migration Android 【发布时间】:2016-06-20 14:11:29 【问题描述】:我们将 Realm 用于我们的应用程序。我们的应用程序已发布测试版。现在我想向我们的一个领域对象添加一个字段。所以我必须写RealmMigration,我也写了一个。这里的问题是如何将此领域迁移应用到我的应用程序。每当我想要某些东西时,我都会使用 Realm.getInstance() 获取领域实例。请记住,Realm.getInstance() 每次都在整个应用程序中使用,我想访问 Realm 数据库。
那么,我对如何应用此迁移有点疑问?任何线索都会有所帮助。谢谢。
我的 RealmMigration 如下。
public class RealmMigrationClass implements RealmMigration
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion)
if(oldVersion == 0)
RealmSchema sessionSchema = realm.getSchema();
if(oldVersion == 0)
RealmObjectSchema sessionObjSchema = sessionSchema.get("Session");
sessionObjSchema.addField("isSessionRecordingUploading", boolean.class, FieldAttribute.REQUIRED)
.transform(new RealmObjectSchema.Function()
@Override
public void apply(DynamicRealmObject obj)
obj.set("isSessionRecordingUploading", false);
);
sessionObjSchema.setNullable("isSessionRecordingUploading",false);
oldVersion++;
public class Session extends RealmObject
@PrimaryKey
private String id;
@Required
private Date date;
private double latitude;
private double longitude;
private String location;
private String note;
private String appVersion;
private String appType;
private String deviceModel;
private HeartRecording heart;
private TemperatureRecording temperature;
private LungsRecording lungs;
@NotNull
private boolean isSessionRecordingUploading;
private boolean sessionInfoUploaded;
private boolean lungsRecordingUploaded;
private boolean heartRecordingUploaded;
从 RealmObject 中删除了 Getter 和 Setter 以缩短问题。当我尝试重新安装应用程序而不卸载前一个应用程序时发生异常。请指教。
【问题讨论】:
【参考方案1】:这里有描述:https://realm.io/docs/java/latest/#migrations
但从本质上讲,您只需提升架构版本并像这样设置配置:
RealmConfiguration config = new RealmConfiguration.Builder(context)
.schemaVersion(2) // Must be bumped when the schema changes
.migration(new MyMigration()) // Migration to run
.build();
Realm.setDefaultConfiguration(config);
// This will automatically trigger the migration if needed
Realm realm = Realm.getDefaultInstance();
【讨论】:
谢谢克里斯蒂安。如果我的理解是正确的,模型将有新创建的字段,所有 Realm.getInstance(this) 将是 Realm.getInstance(configRealm.getDefaultInstance()
或Realm.getInstance(RealmConfiguration)
而不是Realm.getInstance(Context)
。
今天我试过了,我得到“io.realm.exceptions.RealmMigrationNeededException:字段'isSessionRecordingUploading'确实支持现有领域文件中的空值。为字段'isSessionRecordingUploading'使用相应的盒装类型或使用迁移io.realm.internal.Table.convertColumnToNotNullable()" 。我稍微修改了 RealmMigration 类,并在我的领域对象中的字段中添加了 notNull 注释。但我仍然无法克服这个问题。我更新了 Q。
notNull
不是 Realm 理解的注解。从错误看来,布尔值当前是可选的。这意味着您要么必须在 java 类中执行 private Boolean isSessionRecordingUploading
,要么执行以下迁移 realm.getSchema().get("Session").setNullable("isSessionRecordingUploading", false)
我在所有需要它的类中使用getDefaultInstance
方法。但是我的 Application 类中的 init
和 setDefaultConfiguration
方法。我希望这是正确的方法。【参考方案2】:
这是一个帮助类,用于将 .realm 数据库导入我的应用程序。就我而言,我只需要在 android 应用程序中读取从 ios 应用程序创建的数据库
public class RealmImporter
private Activity activity;
private String dbaFullName = "db.realm";
private int rawRealmResource = R.raw.dbRealmResource;
public RealmImporter (Activity activity)
this.activity = activity;
importData();
private RealmConfiguration getConfiguration()
RealmConfiguration config = new RealmConfiguration.Builder()
.name(dbaFullName)
.modules(new MyRealmModule())
.migration(new RealmMigration()
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion)
)
.build();
return config;
public Realm getInstance()
Realm realm = Realm.getInstance(getConfiguration());
return realm;
private void importData()
copyBundledRealmFile(activity.getResources().openRawResource(rawRealmResource), dbaFullName);
Realm.init(activity);
private String copyBundledRealmFile(InputStream inputStream, String outFileName)
try
File file = new File(activity.getFilesDir(), outFileName);
if (file.exists())
return file.getAbsolutePath();
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();
return file.getAbsolutePath();
catch (Exception e)
e.printStackTrace();
return null;
// Create the module
@RealmModule(classes = Artigo.class, Comparativo.class, Indice.class, ItemDeArtigo.class)
public static class MyRealmModule
如果您有多个领域配置(如果您需要导入多个 .realm 文件),则需要创建一个 RealmModule 类并使用 RealmConfiguration.Builder 上的 .modules() 选项。
这是必需的,因为默认情况下,realm 会尝试检查您的 .realm 是否具有用于扩展 RealmObject 的所有模型的表,并且如果您有多个 .realm,则它们中的每一个都会包含您的一些类(但不是全部他们)。
另一个有用的信息是关于 swift 到 android 的迁移。所有 String 属性都必须使用 @Required 注释声明(如果它们具有默认值)。
如果领域给你一个奇怪的异常,比如“在这个模式中找不到这个类”,尝试重新运行你的应用程序,通常它会给出一个不同的错误,这可以暗示真正的问题,尽管有时它只是给出一个随机的错误。
【讨论】:
RealmConfiguration.assetFile()
是不是太主流了?此外,没有“随机错误”,您应该禁用 Instant Run。
嗨,我只是想帮助其他用户。随时根据您的建议编辑我的代码。尽管我禁用了即时运行,但即使我确定文件和表存在,我仍然会遇到诸如“此表/类不存在”之类的随机错误,最终错误是由于“模块”。
我只是说Realm已经有assetFile()
,所以你不必手动编写复制:P
谢谢,但是如果你用你的方法编辑我的代码会更有用,确实更好。以上是关于做Realm Migration Android的正确方法的主要内容,如果未能解决你的问题,请参考以下文章