Realm异步查询的三种方式

阅读过Realm文档的童鞋们应该都知道Realm、RealmObject 和RealmResults 实例都是不可以跨线程使用的。
虽然Realm查询数据的速度非常快,但有些时候我们还是不得不用上异步查询。

在Realm中,从子线程查询到的数据到主线程中是不可以使用的,会报Realm accessed from incorrect thread.
所以我们如果希望通过我们熟悉的方式去创建异步任务,就需要在 子线程创建Realm实例。并且在子线程中将查询到的数据复制一份(普通RealmObject对象,非代理对象)出来。
最开始没有仔细查看Realm的api,自己写了一个CopyUtil,通过反射将代理对象复制成普通对象,效率比较低,后来优化之后好了一点,不过仍然较慢。最近发现realm API自带了copyFromRealm方法将代理对象复制成普通对象,而且非常快。

接下来说说Realm异步查询的三种方式。
第一种结合RxJava,不用自带的asObservable()创建。

//Dao代码:
public Observable<List<SqlCompany>> findAllRx() {
    return Observable.create(new Observable.OnSubscribe<List<SqlCompany>>() {
        @Override
        public void call(Subscriber<? super List<SqlCompany>> subscriber) {
            try {
                Realm realm = Realm.getDefaultInstance();
                RealmResults<SqlCompany> results = realm.where(SqlCompany.class).findAll();
                List<SqlCompany> list = realm.copyFromRealm(results);
                subscriber.onNext(list);
                subscriber.onCompleted();
                realm.close();
            } catch (Exception e) {
                e.printStackTrace();
                subscriber.onError(e);
            }
        }
    });
}

//使用
public void uiSearch() {
    SqlCompanyDao dao = new SqlCompanyDao();
    dao.findAllRx()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<List<SqlCompany>>() {
                @Override
                public void onCompleted() {

                }

                @Override
                public void onError(Throwable e) {

                }

                @Override
                public void onNext(List<SqlCompany> sqlCompanies) {
                    Log.d("SqlCompanyDao", "sqlCompanies:" + sqlCompanies);
                }
            });
}

这种代码比较多,看着比较累~这是最原始的结合Rxjava的方式。

第二种,对以上过程进行封装,不用每次创建新的realm对象,不用每次try catch 和 复制对象。
先贴封装好的工具类,国外牛人封装的,我只是稍微修改了一下。

public abstract class OnSubscribeRealm<T> implements Observable.OnSubscribe<T> {
    private final String fileName;

    private final List<Subscriber<? super T>> subscribers = new ArrayList<>();
    private final AtomicBoolean canceled = new AtomicBoolean();
    private final Object lock = new Object();

    public OnSubscribeRealm() {
        this(null);
    }

    public OnSubscribeRealm(String fileName) {
        this.fileName = fileName;
    }

    @Override
    public void call(final Subscriber<? super T> subscriber) {
        synchronized (lock) {
            boolean canceled = this.canceled.get();
            if (!canceled && !subscribers.isEmpty()) {
                subscriber.add(newUnsubscribeAction(subscriber));
                subscribers.add(subscriber);
                return;
            } else if (canceled) {
                return;
            }
        }
        subscriber.add(newUnsubscribeAction(subscriber));
        subscribers.add(subscriber);

        Realm realm = createRealm();
        boolean withError = false;

        T object = null;
        try {
            if (!this.canceled.get()) {
                realm.beginTransaction();
                object = get(realm);
                if (!this.canceled.get()) {
                    realm.commitTransaction();
                } else {
                    realm.cancelTransaction();
                }
            }
        } catch (RuntimeException e) {
            realm.cancelTransaction();
            sendOnError(new RealmException("Error during transaction.", e));
            withError = true;
        } catch (Error e) {
            realm.cancelTransaction();
            sendOnError(e);
            withError = true;
        }
        if (!this.canceled.get() && !withError) {
            sendOnNext(object);
        }

        try {
            realm.close();
        } catch (RealmException ex) {
            sendOnError(ex);
            withError = true;
        }
        if (!withError) {
            sendOnCompleted();
        }
        this.canceled.set(false);
    }

    private void sendOnNext(T object) {
        for (int i = 0; i < subscribers.size(); i++) {
            Subscriber<? super T> subscriber = subscribers.get(i);
            subscriber.onNext(object);
        }
    }

    private void sendOnError(Throwable e) {
        for (int i = 0; i < subscribers.size(); i++) {
            Subscriber<? super T> subscriber = subscribers.get(i);
            subscriber.onError(e);
        }
    }

    private void sendOnCompleted() {
        for (int i = 0; i < subscribers.size(); i++) {
            Subscriber<? super T> subscriber = subscribers.get(i);
            subscriber.onCompleted();
        }
    }

    @NonNull
    private Subscription newUnsubscribeAction(final Subscriber<? super T> subscriber) {
        return Subscriptions.create(new Action0() {
            @Override
            public void call() {
                synchronized (lock) {
                    subscribers.remove(subscriber);
                    if (subscribers.isEmpty()) {
                        canceled.set(true);
                    }
                }
            }
        });
    }

    public abstract T get(Realm realm);

    // TODO: 2017/7/6 根据需求创建Realm实例
    private Realm createRealm() {
        return Realm.getDefaultInstance();
    }
}
public final class RealmObservable {
    private RealmObservable() {
    }

    public static <T extends Object> Observable<T> createObservable(final Func1<Realm, T> function) {
        return Observable.create(new OnSubscribeRealm<T>() {
            @Override
            public T get(Realm realm) {
                T t = function.call(realm);
                if(t!=null){
                    if (t instanceof RealmObject) {
                        return (T) realm.copyFromRealm((RealmObject)t);
                    } else if (t instanceof RealmList) {
                        return (T) realm.copyFromRealm((List<RealmObject>) t);
                    } else if(t instanceof RealmResults){
                        return (T) realm.copyFromRealm((List<RealmObject>) t);
                    }
                    return t;
                }
                return t;
            }
        });
    }
}

接下来看看如何使用:

//Dao代码:
public void findAll(Subscriber<List<SqlCompany>> subscribe) {
        RealmObservable
                .createObservable(new Func1<Realm, List<SqlCompany>>() {
                    @Override
                    public List<SqlCompany> call(Realm realm) {
                        return realm.where(SqlCompany.class).findAll();
                    }
                })
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(subscribe);
}

//使用:
public void uiSearch2() {
    SqlCompanyDao dao = new SqlCompanyDao();
    dao.findAll(new Subscriber<List<SqlCompany>>() {
        @Override
        public void onCompleted() {

        }

        @Override
        public void onError(Throwable e) {

        }

        @Override
        public void onNext(List<SqlCompany> sqlCompanies) {
            Log.d("SqlCompanyDao", "sqlCompanies:" + sqlCompanies);
        }
    });
}

第三种是使用Realm自己的异步查询:

//Dao代码
//异步获取数据,tip:一定要将结果集声明为全局变量,否则可能会被GC,导致listener不被调用。
RealmResults<SqlCompany> results;
public void findAll(@NonNull RealmChangeListener<RealmResults<SqlCompany>> listener) {
        Realm realm = Realm.getDefaultInstance();
        results = realm.where(SqlCompany.class)
                .findAllAsync();
        results.addChangeListener(listener);
}

//使用
public void uiSearch3() {
    SqlCompanyDao dao = new SqlCompanyDao();
    RealmChangeListener<RealmResults<SqlCompany>>  changeListener = new RealmChangeListener<RealmResults<SqlCompany>>() {

        @Override
        public void onChange(RealmResults<SqlCompany> element) {
            Log.d("SqlCompanyDao", "element:" + element);
            element.removeChangeListener(this);
        }
    };
    dao.findAll(changeListener);
}

这种异步查询方式最简单,且不需要复制对象,所以查出来的对象是代理对象,realm关闭后就不能使用了,且不能直接修改值。
这种方式有一点点小问题,有时候回调执行多次,官方文档显示,数据更新都会执行onChange,如果removeChangeListener,有时候会导致回调不执行。

总结,这三种异步查询,查询速度基本差不多。第二种相对来说最好。
realm也可以结合异步任务,或者其他异步框架来完成异步查询,不过一定要记住在使用线程中创建realm实例,并且要将代理对象复制成普通对象,因为代理对象是不能跨线程使用的。

Tip:

public void uiSearch4() {
    Realm realm = Realm.getDefaultInstance();
    realm.where(SqlCompany.class)
            .findAllAsync()
            .asObservable()
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new DaoSubscribe<RealmResults<SqlCompany>>() {
                @Override
                public void onNext(RealmResults<SqlCompany> sqlCompanies) {
                    Log.d("SqlCompanyDao", "sqlCompanies:" + sqlCompanies);
                }
            });
}

直接使用以上代码进行异步查询是不行的,因为直接查出来的对象没办法跨线程使用,以上代码不存在复制对象的过程,所以会报错。
在实际使用中不要频繁通过Realm realm = Realm.getDefaultInstance();来创建Realm对象,如果只需要同步查询,最好保持app中同一时刻仅有一个realm对象存在,让realm随着app的生命周期创建并释放,或者是使用完,并且复制对象之后立刻关闭realm。

文章内容如有错误欢迎指正,对本文内容有任何疑问欢迎加群讨论:283272067

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值