做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(config) 是的,没错。您应该使用Realm.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 类中的 initsetDefaultConfiguration 方法。我希望这是正确的方法。【参考方案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的正确方法的主要内容,如果未能解决你的问题,请参考以下文章

正确处理Android上的Realm实例

在Realm过滤父母和子女 - Android

Android MVP Architecture and Realm - 如何避免在 MVP 层之间传递上下文?

Android Realm数据库使用

移动端专用数据库Realm介绍

在Android加入和使用Realm