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的环境配置,只需要按照一般教程依赖rxjavarxandroid即可,略过。
  • 注意,目前GreenDao-3.2.2,仅支持RxJava1依赖,如依赖RxJava2将编译错误。
  • 以下通过主要详解insertselect代码(因updatedeleteinsert的原理基本相同,而insertselect两者有一定差异性)。

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的子类PeopleBeanDaoScheduler(指定线程为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 继承自RxBaseRxDao的构造方法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调用了GreenDaoinsertInTx(...)。这里可以看到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线程处理返回的数据,如展示等,故设置SchedulerAndroidSchedulers.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中包装的insertdeleteupdate等方法,最终完成被观察者Observable需要执行的操作;

5.2.1.2 而select则是,先获取Dao,然后调用.queryBuilder()(以及可能存在的条件语句代码,如.where(...)等),到此为止返回的是一个QueryBuilder<T>类型的实例,继续调用QueryBuilder.rx方法创建并返回一个RxQuery<T>类型的实例,再调用RxQuery中包装的.list()方法返回Observable<List<T>>的实例

  • 其中,.rx方法,首先创建了一个Query<T>实例,通过QueryBuilderbuild()方法:
// 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()执行,RxQueryRxBase的子类,还记的之前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,这里原理和任何其他insertdelete等操作均相同
  • 这里这样写,仅是因为这是.subscribe(...)的另一种写法,其中Action1为有参,Action0为无参,这在Rxjava中是一个惯例(xxx1有参,xxx0无参)

6. 结语

  • GreenDao + RxJava非常好用,尤其适合数据量较大的异步操作,以及,查询到数据后使用RxJavamapflatMap对数据进行处理,这样的链式代码看起来好像略多,但结构更清晰便于二次开发和维护,代码灵活便于复用。
  • 个人建议不需要对代码再做二次封装,这样看似需要写的代码少了点,但会破坏Rxjava的灵活性,意义不大。
0 0
原创粉丝点击