说说 Realm 在 Android 上的坑

阅读 1374
收藏 35
2016-11-13
原文链接:blog.csdn.net

1.前言

新项目使用了大名鼎鼎的realm,在网络上看到大量安利realm的文章,但是在使用的过程中却遇到了很多问题,这里记录下两个多月以来遇见的问题。希望大家能够理性选择,不要人云亦云。当然,,realm文档中也给出了一些当前的限制,但是,我们今天要说的,不仅仅是这些。

realm-java文档地址

这里写图片描述

2.线程的限制

通常来讲,我们查询数据库会开一个子线程查询,这次,我们也没例外,简简单单的写两行代码,如下。

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对象只能在他被创建的线程中访问,搞毛啊。即使你查询速度快,也提供了异步查询,可我就想在子线程和主现场之间切换玩。这。。。不是坑么。

3.RealmList的问题

说到这里,我们先来看看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传值就回出现问题,要注意了。

解决办法:我们只能存入数据库,在第二个界面进行查询使用,这不是我想要的。

4.查询出来的数据,intent传值问题

没想到吧,即使简简单单的查询出来的数据,在使用intent传值还是有问题。这次我的实体类变成这样。

public class User extends RealmObject  implements Serializable{
    public String name;
}

接下来,我们将查询出来的数据,使用intent传递到第二个界面。
这里写图片描述

这里写图片描述

细心的同学应该看出来了,这里crash的原因是因为我们userrealmproxy不能被序列化的问题。但是,我们没有传过userrealmproxy啊。这是因为我们查询出来的数据,不再是我们想要的对象,而是realm用apt给我们生成的实体类的子类,或者说是实体类的代理类,在realm中起到作用的,就是这些个代理类。在build目录下我们能找到不少。如图。
这里写图片描述

解决办法,实现cloneable接口,深clone或者是把查询到的值手动复制给我们new的实体类对象。这也不是我们想要的结果

5. RealmChangeListener的坑

这里写图片描述

我们确实需要它来通知我们异步查询什么时候结束。但是,我们要注意后半句,当realmresults对象更新的时候,也会通知我们。

现在,我们思考一个场景,我们要在进入页面的时候,查询数据,显示界面。并且同时请求网络,更新数据,更新界面,没错,这个场景我们确实狠需要,当我们使用RealmChangeListener的时候,RealmChangeListener会通知我们两次,一次是查询数据库完成的,一次是更新的,但是,我们并不需要。因此,大家要做好处理。

6. 数据库版本迁移的问题

从realm文档来看,数据库版本迁移不复杂,但是工作量较大,我们需要知道每次实体类的变更,事实上,我们要做的可能更多,我这里就不模拟迁移了,只给出上次迁移遇到的问题及结果。

新加字段问题
当我们新加字段的时候,如果没有@Required注解,那么我们需要设置.setNullable(字段,false) ,没错,就是false。按理来说,我们不是应该为true么?但是,通过追源码得知。设置true,会抛出throw new IllegalStateException("Field is already required: " + fieldName);

但是要注意,新加的类不需要setnullable

上周四血的教训!!!

7 总结

由于使用时间较短,暂时只发现这个。最后,选择realm,一定要考虑清楚是否真的需要,是否想好了怎么处理这些问题。

评论