GreenDao
+ RxJava
的应用示例。RxJava
的用法,后面会找时间,开一篇单独的文章对RxJava
做介绍。官方github:
https://github.com/ReactiveX/RxJava
https://github.com/ReactiveX/RxAndroid
绝对精制干货:
http://gank.io/post/560e15be2dca930e00da1083
- 如果第一遍看不懂,那么就尝试先敲一遍文章中代码;
- 如果第二遍看不懂,那么就尝试脱离这篇文章写一些demo;
- 看到第三遍的时候,基本就懂了,然后尽情的用吧,用完了记得常回头看看还会有收获。
RxJava
的环境配置,只需要按照一般教程依赖rxjava
、rxandroid
即可,略过。GreenDao-3.2.2
,仅支持RxJava1
依赖,如依赖RxJava2
将编译错误。insert
、select
代码(因update
、delete
和insert
的原理基本相同,而insert
、select
两者有一定差异性)。假设有如下场景:
PeopleBean
数据(List
)List peopleBeanList = new ArrayList();
for (int i = 0; i < 5; i++) {
//随机生成的数据,可忽略
PeopleBean peopleBean = new PeopleBean();
peopleBean.setAge((int) (Math.random() * 100 + 10));
peopleBean.setIdCard((int) (Math.random() * 1000000) + "01212221231" + i);
if (Math.random() * 100 > 50) {
peopleBean.setIsMale(true);
} else {
peopleBean.setIsMale(false);
}
peopleBean.setName("Jams" + (int) (Math.random() * 1000));
peopleBean.setProvince("honghuangzhiqian");
peopleBeanList.add(peopleBean);
}
//数据库操作
GreenDaoUtil.getDaoSession(activity)
.getPeopleBeanDao()
.rx()
.insertInTx(peopleBeanList)
.map(new Func1, List>() {
@Override
public List call(Iterable it) {
return (List) it;
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber>() {
@Override
public void onStart() {
super.onStart();
}
@Override
public void onNext(List peopleBeanList) {
//TODO 这里做下一步操作,如将数据展示在UI等
}
@Override
public void onError(Throwable e) {
}
@Override
public void onCompleted() {
}
});
因为是流式代码,所以很容易做到一行一行解释。当然看起来代码挺多,但是,结构非常清晰
PeopleBeanDao
GreenDaoUtil.getDaoSession(activity)
.getPeopleBeanDao()
这是自己封装的一个单例的,加synchronized的方法
PeopleBeanDao
转为RxDao
.rx()
调用.rx()
,源码分析:
AbstractDao
的子类PeopleBeanDao
和Scheduler
(指定线程为io)作为参数 new
一个 RxDao
://AbstractDao.java中
......
@Experimental
public RxDao<T, K> rx() {
if (rxDao == null) {
rxDao = new RxDao<>(this, Schedulers.io());
}
return rxDao;
}
......
RxDao
继承自RxBase
,RxDao
的构造方法super
自父类,在RxBase
内对被观察者Observable
设定传入的Scheduler
(io)://RxDao.java中
......
@Experimental
public RxDao(AbstractDao dao, Scheduler scheduler) {
super(scheduler);
this.dao = dao;
}
......
//RxBase.java中
......
@Experimental
RxBase(Scheduler scheduler) {
this.scheduler = scheduler;
}
/**
* The default scheduler (or null) used for wrapping.
*/
@Experimental
public Scheduler getScheduler() {
return scheduler;
}
protected Observable wrap(Callable callable) {
return wrap(RxUtils.fromCallable(callable));
}
protected Observable wrap(Observable observable) {
if (scheduler != null) {
return observable.subscribeOn(scheduler);
} else {
return observable;
}
}
RxDao
源码包含众多GreenDao
的Dao操作方法(增删改,注意:不包含查)的封装,返回的是”被观察者”Observable
包装类,截取部分如下://RxDao.java中
......
/**
* Rx version of {@link AbstractDao#insertInTx(Iterable)} returning an Observable.
* Note that the Observable will emit the given entities back to its subscribers.
*/
@Experimental
public Observable> insertInTx(final Iterable entities) {
return wrap(new Callable>() {
@Override
public Iterable call() throws Exception {
dao.insertInTx(entities);
return entities;
}
});
}
......
.insertInTx(...)
.insertInTx(peopleBeanList)
insertInTx(...)
方法,可以看到其中的本质仍是peopleBeanDao
调用了GreenDao
的insertInTx(...)
。这里可以看到return wrap(...)
,这个wrap(...)
正是上面提到的RxDao
的父类RxBase
中用来创建被观察者Observable
,并给被Observable
设置线程为io
的方法://RxDao.java中
......
/**
* Rx version of {@link AbstractDao#insertInTx(Iterable)} returning an Observable.
* Note that the Observable will emit the given entities back to its subscribers.
*/
@Experimental
public Observable> insertInTx(final Iterable entities) {
return wrap(new Callable>() {
@Override
public Iterable call() throws Exception {
dao.insertInTx(entities);
return entities;
}
});
}
......
//RxBase.java中
......
//调用RxUtils.fromCallable(...)创建一个Observable,调用wrap(...)指定线程为io
protected Observable wrap(Callable callable) {
return wrap(RxUtils.fromCallable(callable));
}
protected Observable wrap(Observable observable) {
if (scheduler != null) {
return observable.subscribeOn(scheduler);
} else {
return observable;
}
}
map(...)
,因insertInTx(...)
返回的是Observable>
,我这里常用的是List
,故使用Rxjava的map
,做了个一对一的转换
。可以详细了解Rxjava
的教程,另外还有flatMap
是一对多的转换
,都非常实用。这一步在我的demo中其实可以不使用,直接在onNext
强转,这里为了演示添加了进来。.map(new Func1<Iterable<PeopleBean>, List<PeopleBean>>() {
@Override
public List<PeopleBean> call(Iterable<PeopleBean> it) {
return (List<PeopleBean>) it;
}
})
observeOn(...))
,指定观察者(或订阅者)subscribe
所在的线程.observeOn(AndroidSchedulers.mainThread())
Scheduler
为AndroidSchedulers.mainThread()
Schedulers.io
,故不再需要指定数据库操作的线程subscribe(...)
,处理被观察者Observable
发送过来的数据:.subscribe(new Subscriber>() {
@Override
public void onStart() {
super.onStart();
}
@Override
public void onNext(List peopleBeanList) {
//TODO 这里做下一步操作,如将数据展示在UI等
}
@Override
public void onError(Throwable e) {
}
@Override
public void onCompleted() {
}
});
onStart()
经测试,被认为是永远在UI主线程的,可以在这里做一些准备工作,如启动一个dialog
做loading之类onNext(...)
中,如处理数据结果,之前已指定为主线程.observeOn(AndroidSchedulers.mainThread())
onCompleted()
无返回值,仅作为一个结束的状态,在这里可以做如dismiss
dialog
的操作.subscribe()
,并且不需要指定在主线程操作的代码.observeOn(AndroidSchedulers.mainThread())
即可。因为是插入数据,一般也不需要处理新插入的数据: .subscribe();
查询之前insert
的数据。如,年龄大于30,并且是男性:
GreenDaoUtil.getDaoSession(activity)
.getPeopleBeanDao()
.queryBuilder()
.where(PeopleBeanDao.Properties.Age.gt(30), PeopleBeanDao.Properties.IsMale.eq(true))
.rx()
.list()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
new Action1>() {
@Override
public void call(List peopleBeen) {
//TODO 这里做下一步操作,如将数据展示在UI等
}
},
new Action1() {
@Override
public void call(Throwable throwable) {
throwable.printStackTrace();
}
}
);
主要分析下同insert
等有差异的部分:
Observable
执行select
.queryBuilder()
.where(PeopleBeanDao.Properties.Age.gt(30), PeopleBeanDao.Properties.IsMale.eq(true))
.rx()
.list()
insert
等的写法是先获取Dao
,然后调用.rx
,创建一个RxDao
,之后调用RxDao
中包装的insert
、delete
、update
等方法,最终完成被观察者Observable
需要执行的操作;select
则是,先获取Dao
,然后调用.queryBuilder()
(以及可能存在的条件语句代码,如.where(...)
等),到此为止返回的是一个QueryBuilder
类型的实例,继续调用QueryBuilder
中.rx
方法创建并返回一个RxQuery
类型的实例,再调用RxQuery
中包装的.list()
方法返回Observable>
的实例.rx
方法,首先创建了一个Query
实例,通过QueryBuilder
的build()
方法:// QueryBuilder.java中
...
/**
* Shorthand for {@link QueryBuilder#build() build()}.{@link Query#__InternalRx()}.
*/
@Experimental
public RxQuery<T> rx() {
return build().__InternalRx();
}
...
/**
* Builds a reusable query object (Query objects can be executed more efficiently than creating a QueryBuilder for
* each execution.
*/
public Query<T> build() {
StringBuilder builder = createSelectBuilder();
int limitPosition = checkAddLimit(builder);
int offsetPosition = checkAddOffset(builder);
String sql = builder.toString();
checkLog(sql);
return Query.create(dao, sql, values.toArray(), limitPosition, offsetPosition);
}
...
.rx
方法中,又调用了Query
中的__InternalRx
方法,创建了一个RxQuery
的实例,并指定当前代码是在Schedulers.io()
执行,RxQuery
是RxBase
的子类,还记的之前insert
所用到的RxDao
同样是RxBase
的子类://Query.java中
...
/**
* DO NOT USE.
* The returned {@link RxTransaction} allows getting query results using Rx Observables using RX's IO scheduler for
* subscribeOn.
*
* @see #__internalRxPlain()
*/
@Internal
public RxQuery __InternalRx() {
if (rxTxIo == null) {
rxTxIo = new RxQuery(this, Schedulers.io());
}
return rxTxIo;
}
//RxQuery.java中
/**
* Gets {@link org.greenrobot.greendao.query.Query} results in Rx fashion.
*/
@Experimental
// TODO Pass parameters: currently, parameters are always set to their initial values because of forCurrentThread()
public class RxQuery<T> extends RxBase {
private final Query query;
public RxQuery(Query query) {
this.query = query;
}
public RxQuery(Query query, Scheduler scheduler) {
super(scheduler);
this.query = query;
}
/**
* Rx version of {@link Query#list()} returning an Observable.
*/
@Experimental
public Observable> list() {
return wrap(new Callable>() {
@Override
public List call() throws Exception {
return query.forCurrentThread().list();
}
});
}
...
Observer
(订阅者subscribe
)接收结果.subscribe(
new Action1>() {
@Override
public void call(List peopleBeen) {
//TODO 等同onNext()这里做下一步操作,如将数据展示在UI等
}
},
new Action1() {
@Override
public void call(Throwable throwable) {
throwable.printStackTrace();
}
}
, new Action0() {
@Override
public void call() {
//等同onCompleted()
}
}
);
Observable
被订阅,将在查询结果获得时,通知订阅者subscribe
,这里原理和任何其他insert
、delete
等操作均相同.subscribe(...)
的另一种写法,其中Action1
为有参,Action0
为无参,这在Rxjava
中是一个惯例(xxx1
有参,xxx0
无参)GreenDao
+ RxJava
非常好用,尤其适合数据量较大的异步操作,以及,查询到数据后使用RxJava
的map
或flatMap
对数据进行处理,这样的链式代码看起来好像略多,但结构更清晰便于二次开发和维护,代码灵活便于复用。Rxjava
的灵活性,意义不大。