新项目使用了大名鼎鼎的realm,在网络上看到大量安利realm的文章,但是在使用的过程中却遇到了很多问题,这里记录下两个多月以来遇见的问题。希望大家能够理性选择,不要人云亦云。当然,,realm文档中也给出了一些当前的限制,但是,我们今天要说的,不仅仅是这些。
realm-java文档地址
通常来讲,我们查询数据库会开一个子线程查询,这次,我们也没例外,简简单单的写两行代码,如下。
realm.where(User.class)
.findAll()
.asObservable()
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1>() {
@Override
public void call(RealmResults users) {
users.get(0);
}
});
当我们满心欢喜的运行起来的时候,mdzz,crash了。日志如下。
what,realm对象只能在他被创建的线程中访问,搞毛啊。即使你查询速度快,也提供了异步查询,可我就想在子线程和主现场之间切换玩。这。。。不是坑么。
很好,基本能满足我们的需求。并且还提供了RealmList来提供给我们使用。这次,我要实现这样的需求,在a1中查询数据出来,传递到a2中,由于实体较复杂,内含多对一的对应关系,这里是用RealmList来处理了。写好代码,不幸的是,又crash了。
这次的错误,预示这我们,realm intent传值又问题。
我们的实体对象是这样写的。
public class User extends RealmObject implements Serializable{
public String name;
public RealmList cursors;
}
忽略我不标准的写法
很明显,问题出在了realmlist这里,我们进去一看,fuck,和arraylist的区别很明显。如图。
可以看到,arraylist是实现的Serializable接口的,而realmlist却没有,因此,我们在使用用了realmlist的实体类的时候,使用intent传值就回出现问题,要注意了。
解决办法:我们只能存入数据库,在第二个界面进行查询使用,这不是我想要的。
没想到吧,即使简简单单的查询出来的数据,在使用intent传值还是有问题。这次我的实体类变成这样。
public class User extends RealmObject implements Serializable{
public String name;
}
接下来,我们将查询出来的数据,使用intent传递到第二个界面。
细心的同学应该看出来了,这里crash的原因是因为我们userrealmproxy不能被序列化的问题。但是,我们没有传过userrealmproxy啊。这是因为我们查询出来的数据,不再是我们想要的对象,而是realm用apt给我们生成的实体类的子类,或者说是实体类的代理类,在realm中起到作用的,就是这些个代理类。在build目录下我们能找到不少。如图。
解决办法,实现cloneable接口,深clone或者是把查询到的值手动复制给我们new的实体类对象。这也不是我们想要的结果
我们确实需要它来通知我们异步查询什么时候结束。但是,我们要注意后半句,当realmresults对象更新的时候,也会通知我们。
现在,我们思考一个场景,我们要在进入页面的时候,查询数据,显示界面。并且同时请求网络,更新数据,更新界面,没错,这个场景我们确实狠需要,当我们使用RealmChangeListener的时候,RealmChangeListener会通知我们两次,一次是查询数据库完成的,一次是更新的,但是,我们并不需要。因此,大家要做好处理。
从realm文档来看,数据库版本迁移不复杂,但是工作量较大,我们需要知道每次实体类的变更,事实上,我们要做的可能更多,我这里就不模拟迁移了,只给出上次迁移遇到的问题及结果。
新加字段问题
当我们新加字段的时候,如果没有@Required注解,那么我们需要设置.setNullable(字段,false)
,没错,就是false。按理来说,我们不是应该为true么?但是,通过追源码得知。设置true,会抛出throw new IllegalStateException("Field is already required: " + fieldName);
但是要注意,新加的类不需要setnullable
上周四血的教训!!!
由于使用时间较短,暂时只发现这个。最后,选择realm,一定要考虑清楚是否真的需要,是否想好了怎么处理这些问题。