GreenDao教程(4):GreenDao + RxJava
来源:互联网 发布:mac照片文件夹在哪里 编辑:程序博客网 时间:2024/03/28 22:27
1. 前言
- 主要介绍
GreenDao
+RxJava
的应用示例。 - 这里不会详细介绍
RxJava
的用法,后面会找时间,开一篇单独的文章对RxJava
做介绍。 - 本文资料来源网络公开资源,并根据个人实践见解纯手打整理,如有错误请随时指出。
- 本文主要用于个人积累及分享,文中可能引用其他技术大牛文章(仅引用链接不转载),如有侵权请告知必妥善处理。
2. 关于RxJava
官方github:
https://github.com/ReactiveX/RxJava
https://github.com/ReactiveX/RxAndroid绝对精制干货:
http://gank.io/post/560e15be2dca930e00da1083
- 如果第一遍看不懂,那么就尝试先敲一遍文章中代码;
- 如果第二遍看不懂,那么就尝试脱离这篇文章写一些demo;
- 看到第三遍的时候,基本就懂了,然后尽情的用吧,用完了记得常回头看看还会有收获。
3. 简介
RxJava
的环境配置,只需要按照一般教程依赖rxjava
、rxandroid
即可,略过。- 注意,目前
GreenDao-3.2.2
,仅支持RxJava1
依赖,如依赖RxJava2
将编译错误。 - 以下通过主要详解
insert
、select
代码(因update
、delete
和insert
的原理基本相同,而insert
、select
两者有一定差异性)。
4. insert
4.1. 示例
假设有如下场景:
- 需要向数据库插入一组
PeopleBean
数据(List<PeopleBean>
) - 插入完成后返回插入成功的数据,并执行下一步操作
List<PeopleBean> peopleBeanList = new ArrayList<PeopleBean>();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<Iterable<PeopleBean>, List<PeopleBean>>() { @Override public List<PeopleBean> call(Iterable<PeopleBean> it) { return (List<PeopleBean>) it; } }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<List<PeopleBean>>() { @Override public void onStart() { super.onStart(); } @Override public void onNext(List<PeopleBean> peopleBeanList) { //TODO 这里做下一步操作,如将数据展示在UI等 } @Override public void onError(Throwable e) { } @Override public void onCompleted() { } });
4.2. 代码分析
因为是流式代码,所以很容易做到一行一行解释。当然看起来代码挺多,但是,结构非常清晰
4.2.1. 获取PeopleBeanDao
GreenDaoUtil.getDaoSession(activity) .getPeopleBeanDao()
这是自己封装的一个单例的,加synchronized的方法
4.2.2. 将PeopleBeanDao
转为RxDao
.rx()
调用.rx()
,源码分析:
4.2.2.1. 使用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; }......
4.2.2.2. RxDao
继承自RxBase
,RxDao
的构造方法super
自父类,在RxBase
内对被观察者Observable
设定传入的Scheduler
(io):
//RxDao.java中......@Experimental public RxDao(AbstractDao<T, K> 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 <R> Observable<R> wrap(Callable<R> callable) { return wrap(RxUtils.fromCallable(callable)); } protected <R> Observable<R> wrap(Observable<R> observable) { if (scheduler != null) { return observable.subscribeOn(scheduler); } else { return observable; } }
4.2.2.3. 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<Iterable<T>> insertInTx(final Iterable<T> entities) { return wrap(new Callable<Iterable<T>>() { @Override public Iterable<T> call() throws Exception { dao.insertInTx(entities); return entities; } }); }......
4.2.3. 调用.insertInTx(...)
.insertInTx(peopleBeanList)
4.2.3.1. 进入RxDao.java中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<Iterable<T>> insertInTx(final Iterable<T> entities) { return wrap(new Callable<Iterable<T>>() { @Override public Iterable<T> call() throws Exception { dao.insertInTx(entities); return entities; } }); }......
//RxBase.java中......//调用RxUtils.fromCallable(...)创建一个Observable,调用wrap(...)指定线程为io protected <R> Observable<R> wrap(Callable<R> callable) { return wrap(RxUtils.fromCallable(callable)); } protected <R> Observable<R> wrap(Observable<R> observable) { if (scheduler != null) { return observable.subscribeOn(scheduler); } else { return observable; } }
4.2.3.2. 调用map(...)
,因insertInTx(...)
返回的是Observable<Iterable<PeopleBean>>
,我这里常用的是List<PeopleBean>
,故使用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; }})
4.2.3.3. 调用observeOn(...))
,指定观察者(或订阅者)subscribe
所在的线程
.observeOn(AndroidSchedulers.mainThread())
- 这里android一般在UI线程处理返回的数据,如展示等,故设置
Scheduler
为AndroidSchedulers.mainThread()
- 因为RxBase中已设置了数据库操作所在的线程为
Schedulers.io
,故不再需要指定数据库操作的线程
4.2.3.4. 调用subscribe(...)
,处理被观察者Observable
发送过来的数据:
.subscribe(new Subscriber<List<PeopleBean>>() { @Override public void onStart() { super.onStart(); } @Override public void onNext(List<PeopleBean> 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();
5 . select
5.1. 示例
查询之前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<List<PeopleBean>>() { @Override public void call(List<PeopleBean> peopleBeen) { //TODO 这里做下一步操作,如将数据展示在UI等 } }, new Action1<Throwable>() { @Override public void call(Throwable throwable) { throwable.printStackTrace(); } } );
5.2. 代码分析
主要分析下同insert
等有差异的部分:
5.2.1. 被观察者Observable
执行select
.queryBuilder().where(PeopleBeanDao.Properties.Age.gt(30), PeopleBeanDao.Properties.IsMale.eq(true)).rx().list()
5.2.1.1 insert
等的写法是先获取Dao
,然后调用.rx
,创建一个RxDao
,之后调用RxDao
中包装的insert
、delete
、update
等方法,最终完成被观察者Observable
需要执行的操作;
5.2.1.2 而select
则是,先获取Dao
,然后调用.queryBuilder()
(以及可能存在的条件语句代码,如.where(...)
等),到此为止返回的是一个QueryBuilder<T>
类型的实例,继续调用QueryBuilder
中.rx
方法创建并返回一个RxQuery<T>
类型的实例,再调用RxQuery
中包装的.list()
方法返回Observable<List<T>>
的实例
- 其中,
.rx
方法,首先创建了一个Query<T>
实例,通过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<T> query; public RxQuery(Query<T> query) { this.query = query; } public RxQuery(Query<T> query, Scheduler scheduler) { super(scheduler); this.query = query; } /** * Rx version of {@link Query#list()} returning an Observable. */ @Experimental public Observable<List<T>> list() { return wrap(new Callable<List<T>>() { @Override public List<T> call() throws Exception { return query.forCurrentThread().list(); } }); }...
5.2.2. 观察者Observer
(订阅者subscribe
)接收结果
.subscribe( new Action1<List<PeopleBean>>() { @Override public void call(List<PeopleBean> peopleBeen) { //TODO 等同onNext()这里做下一步操作,如将数据展示在UI等 } }, new Action1<Throwable>() { @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
无参)
6. 结语
GreenDao
+RxJava
非常好用,尤其适合数据量较大的异步操作,以及,查询到数据后使用RxJava
的map
或flatMap
对数据进行处理,这样的链式代码看起来好像略多,但结构更清晰便于二次开发和维护,代码灵活便于复用。- 个人建议不需要对代码再做二次封装,这样看似需要写的代码少了点,但会破坏
Rxjava
的灵活性,意义不大。
0 0
- GreenDao教程(4):GreenDao + RxJava
- GreenDao 教程
- greenDao
- greenDAO
- greenDAO
- greenDAO
- GreenDao
- GreenDAO
- Greendao
- greenDAO
- GreenDao
- GreenDao
- GreenDao
- GreenDAO
- GreenDAO
- GreenDao
- GreenDao
- greenDAO
- SQL保存单引号
- 实现BPS组织机构数据权限分离解决方案
- HTTP 协议详解
- DTcmsV4.0分析学习——(2)系统框架
- Android解包命令:unpackbootimg与打包命令:mkbootimg使用
- GreenDao教程(4):GreenDao + RxJava
- Android MVP模式简单例子实战
- opencv3.2.0,contirb,cmake所碰到的一些问题记录
- Error:org.gradle.api.internal.tasks.DefaultTaskInputs$TaskInputUnionFileCollection cannot be cast to
- Relative Ranks
- 静态redisTemplate的注入
- pclint vc2012配置
- 自动化分层测试基础
- dp专题 第十三题 最大上升子序列的和