Rxjava学习

来源:互联网 发布:js二级联动省市代码 编辑:程序博客网 时间:2024/05/18 06:11

最近公司项目使用了rxjava,学习了之后感觉真的很很厉害。RxJava 在 GitHub 主页上的自我介绍是 "a library for composing asynchronous and event-based programs using observable sequences for the Java VM",意思是“一个可以在JVM上使用可观察序列来组成异步的和基于事件的程序的库”。在学习之前先了解了解几个单词的字面含义,不然看到后面太晕了。

observable 可观察者,即被观察者nobserver   观察者nobserve    观察vsubscriber 订阅者nsubscribe  订阅vscheduler  调度程序n

我理解rxjava最基本也是最普通的用法就是:observable.subscribe(subscriber);翻成中文就是“被观察者.subscribe(订阅者)”;

在被观察者做的操作处理的事情在订阅发生后都是要“告诉”或者说传递给订阅者,然后订阅者里再执行回调代码。

基本用法

第一步:我们可以先创建一个被观察者

 //创建一个被订阅者 creat        final Observable observable = Observable.create(new Observable.OnSubscribe<String>() {            @Override            public void call(Subscriber<? super String> subscriber) {                subscriber.onNext("沐小奶:闪光");                subscriber.onNext("沐小奶他哥:老子跑~");                subscriber.onNext("沐小奶:束缚");                subscriber.onNext("沐小奶他哥:切弓射~");                subscriber.onNext("沐小奶:痛苦锁链");                subscriber.onNext("沐小奶他哥:黄药,准备30s后解掉痛苦,结果~~");                subscriber.onNext("沐小奶:暴风祈祷,霹雳!!暴击!!!");                subscriber.onNext("沐小奶他哥:你麻痹!");                subscriber.onNext("沐小奶他哥,卒!");                subscriber.onCompleted();            }        });
而且创建observable的方法有很多,官方文档里提供的有这些

just( ) — 将一个或多个对象转换成发射这个或这些对象的一个Observablefrom( ) — 将一个Iterable, 一个Future, 或者一个数组转换成一个Observablerepeat( ) — 创建一个重复发射指定数据或数据序列的ObservablerepeatWhen( ) — 创建一个重复发射指定数据或数据序列的Observable,它依赖于另一个Observable发射的数据create( ) — 使用一个函数从头创建一个Observabledefer( ) — 只有当订阅者订阅才创建Observable;为每个订阅创建一个新的Observablerange( ) — 创建一个发射指定范围的整数序列的Observableinterval( ) — 创建一个按照给定的时间间隔发射整数序列的Observabletimer( ) — 创建一个在给定的延时之后发射单个数据的Observableempty( ) — 创建一个什么都不做直接通知完成的Observableerror( ) — 创建一个什么都不做直接通知错误的Observablenever( ) — 创建一个不发射任何数据的Observable
再列举一下just 和from 的用法

/**         * 创建一个被订阅者 just         * just(T...): 将传入的参数依次发送出来。         *                onNext("沐小奶:闪光");         *                onNext("沐小奶他哥:老子跑~");         *                onCompleted();         *                将会依次调用:         */        final Observable observable = Observable.just(                "沐小奶:闪光",                "沐小奶他哥:老子跑~"        );
就如说明里显示的那样 ,他内部实际已经依次发射出“沐小奶:闪光”,“沐小奶他哥;老子跑~”和creat感觉意思是一样的

再看看from的

/**     * 创建一个被订阅者 from     * from(T[]) / from(Iterable<? extends T>) : 将传入的数组或 Iterable 拆分成具体对象后,依次发送出来。     * 将会依次调用:       onNext("弈剑听雨");       onNext("秀川");       onNext("张小凡");       onNext("鬼厉");       onNext("天问");       onCompleted();       */        String[] names = {"弈剑听雨", "秀川", "张小凡", "鬼厉", "天问"};        final Observable observable = Observable.from(names);
都是一样的。

 第二步 :创建一个订阅者

//创建一个订阅者        final Subscriber<String> subscriber = new Subscriber<String>() {            @Override            public void onCompleted() {                // 订阅事件被触发 并完成            }            @Override            public void onError(Throwable e) {                // 订阅事件触发后 出现错误            }            @Override            public void onNext(String s) {                //订阅事件发出时 所执行的回调代码                XgoLog.i("======== " + s);                Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();            }        };
事实上,subscribe()还支持不完整定义的回调,于是可以看看下面的例子

 /**         * Action1 也是一个接口,它同样只有一个方法call(T param),这个方法也无返回值,但有一个参数;         * 与 Action0 同理,由于 onNext(T obj) 和 onError(Throwable error)也是单参数无返回值的,         * 因此 Action1 可以将 onNext(obj) 和 onError(error) 打包起来传入 subscribe() 以实现不完整定义的<span style="white-space:pre"></span>   回调。         * 事实上,虽然 Action0 和 Action1 在 API 中使用最广泛,但 RxJava 是提供了多个 ActionX 形式的接口         * (例如Action2, Action3) 的,它们可以被用以包装不同的无返回值的方法。         */        final Action1<String> onNextAction = new Action1<String>() {            @Override            public void call(String s) {                XgoLog.i("======== " + s);                Toast.makeText(MainActivity.this, s, Toast.LENGTH_SHORT).show();            }        };        final Action1<Throwable> onErrorAction = new Action1<Throwable>() {            @Override            public void call(Throwable s) {            }        };        /**         * Action0 是 RxJava 的一个接口,它只有一个方法 call(),这个方法是无参无返回值的;         * 由于 onCompleted() 方法也是无参无返回值的,因此 Action0 可以被当成一个包装对象,         * 将 onCompleted()的内容打包起来将自己作为一个参数传入 subscribe() 以实现不完整定义的回调。         * 这样其实也可以看做将 onCompleted() 方法作为参数传进了 subscribe(),相当于其他某些语言中的『闭包』。         */        final Action0 onCompletedAction = new Action0() {            @Override            public void call() {                XgoLog.i("======== completed");            }        };
值得注意的是 Action0,Action1 方法,后面还有ActionX等,上面的注释解释的很清楚了, 不严谨的我梦可以简单的理解Action0折方法无参无返回值,Action1 一个参无返回值这样。

第三步:订阅

               <span style="white-space:pre"></span>  observable.subscribe(subscriber);//                observable.subscribe(onNextAction, onErrorAction, onCompletedAction);//                observable.subscribe(onNextAction, onErrorAction);//                observable.subscribe(onNextAction);
这样一次基本的Rxjava使用就完成了。

线程

 /**     * 由 id 取得图片并显示     * 由指定的一个drawable文件id drawableRes取得图片,并显示在ImageView中,并在出现异常的时候打印 Toast报错     * Schedulers.immediate(): 直接在当前线程运行,相当于不指定线程。这是默认的 Scheduler。     * Schedulers.newThread(): 总是启用新线程,并在新线程执行操作。     * Schedulers.io(): I/O 操作(读写文件、读写数据库、网络信息交互等)所使用的 Scheduler。行为模式和 newTh       read() 差不多,区别在于 io() 的内部实现是是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下          io() 比 newThread()更有效率。不要把计算工作放在 io() 中,可以避免创建不必要的线程。     * Schedulers.computation(): 计算所使用的 Scheduler。这个计算指的是 CPU 密集型计算,即不会被 I/O 等操作       限制性能的操作,例如图形的计算。这个 Scheduler 使用的固定的线程池,大小为 CPU 核数。不要把 I/O 操作放在        computation() 中,否则 I/O 操作的等待时间会浪费 CPU。     * 另外, Android 还有一个专用的 AndroidSchedulers.mainThread(),它指定的操作将在 Android 主线程运行。     * 有了这几个 Scheduler ,就可以使用 subscribeOn() 和 observeOn() 两个方法来对线程进行控制了。     * <p/>     * subscribeOn(): 指定 subscribe() 所发生的线程,即 Observable.OnSubscribe 被激活时所处的线程。或者叫       做事件产生的线程。     * observeOn(): 指定 Subscriber 所运行在的线程。或者叫做事件消费的线程。     */    private void demo3() {        final ImageView imageView = (ImageView) findViewById(R.id.iv_drawable);        Observable.create(new Observable.OnSubscribe<Drawable>() {            @Override            public void call(Subscriber<? super Drawable> subscriber) {                Drawable drawable = getResources().getDrawable(R.drawable.act_splash);                subscriber.onNext(drawable);                subscriber.onCompleted();            }        })                .subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程                .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程                .subscribe(new Subscriber<Drawable>() {                    @Override                    public void onCompleted() {                    }                    @Override                    public void onError(Throwable e) {                        Toast.makeText(MainActivity.this, "ERROR!!", Toast.LENGTH_SHORT).show();                    }                    @Override                    public void onNext(Drawable drawable) {                        imageView.setImageDrawable(drawable);                    }                });    }

注释里已经大致解说了一下scheduler 相关的api,除此Android 还有一个专用的 AndroidSchedulers.mainThread(),它指定的操作将在 Android 主线程运行。有了这几个 scheduler,就可以使用 subscribeOn() 和 observeOn() 两个方法来对线程进行控制了。
subscribeOn(): 指定 subscribe() 所发生的线程,即 Observable.OnSubscribe 被激活时所处的线程。或者叫做事件产生的线程。
observeOn(): 指定 Subscriber 所运行在的线程。或者叫做事件消费的线程。
例如刚刚的例子:
上面这段代码中,由于 subscribeOn(Schedulers.io()) 的指定,被创建的事件的内容drawable加载图片将会在 IO 线程发出;而由于observeOn(AndroidScheculers.mainThread()) 的指定,因此 subscriber 图片的显示将发生在主线程 。事实上,这种在subscribe() 之前写上两句 subscribeOn(Scheduler.io()) 和 observeOn(AndroidSchedulers.mainThread()) 的使用方式非常常见,它适用于多数的 『后台线程取数据,主线程显示』的程序策略。

切换线程
既然可以利用 subscribeOn() 结合 observeOn() 来实现线程控制,让事件的产生和消费发生在不同的线程。那么可不可以多切换几次线程呢?可以的。
observeOn() 指定的是 Subscriber 的线程,而这个 Subscriber 并不一定是subscribe() 参数中的 Subscriber ,而是 observeOn() 执行时的当前 Observable 所对应的Subscriber ,即它的直接下级 Subscriber 。换句话说,observeOn() 指定的是它之后的操作所在的线程。因此如果有多次切换线程的需求,只要在每个想要切换线程的位置调用一次 observeOn() 即可。
通过 observeOn() 的多次调用,程序实现了线程的多次切换。不过,不同于 observeOn() , subscribeOn() 的位置放在哪里都可以,但它是只能调用一次的。why?




subscribeOn() 和 observeOn() 都做了线程切换的工作(图中的 "schedule" 部位)。不同的是,subscribeOn() 的线程切换发生在 OnSubscribe 中,即在它通知上一级 OnSubscribe 时,这时事件还没有开始发送,因此subscribeOn() 的线程控制可以从事件发出的开端就造成影响;而 observeOn() 的线程切换则发生在它内建的 Subscriber 中,即发生在它即将给下一级 Subscriber 发送事件时,因此 observeOn() 控制的是它后面的线程。

doOnSubscribe()

有一个方法 Observable.doOnSubscribe() ,是在subscribe() 调用后而且在事件发送前执行,它可以指定线程。默认情况下, doOnSubscribe() 执行在 subscribe()发生的线程;而如果在 doOnSubscribe() 之后有 subscribeOn() 的话,它将执行在离它最近的 subscribeOn() 所指定的线程。


Observable.just(“吸血波”, “地震波”, “枯竭波”, “压力波”) // IO 线程,由 subscribeOn() 指定    .subscribeOn(Schedulers.io())    .observeOn(Schedulers.newThread())    .map(mapOperator) // 新线程,由 observeOn() 指定    .observeOn(Schedulers.io())    .map(mapOperator2) // IO 线程,由 observeOn() 指定    .observeOn(AndroidSchedulers.mainThread)     .subscribe(subscriber);  // Android 主线程,由 observeOn() 指定
int drawableRes = R.mipmap.malin;        Observable.create(new Observable.OnSubscribe<Drawable>() { //1:被观察者            @Override            public void call(Subscriber<? super Drawable> subscriber) {                Logger.d("被观察者");                Drawable drawable = getResources().getDrawable(drawableRes);                subscriber.onNext(drawable);                subscriber.onCompleted();            }        })                .subscribeOn(Schedulers.io())//事件产生的线程。指定 subscribe() 发生在 IO 线程                // doOnSubscribe() 之后有 subscribeOn() 的话,它将执行在离它最近的 subscribeOn() 所指定的线程。这里将执行在主线程中                .doOnSubscribe(new Action0() {                    @Override                    public void call() {                        if (mProgressBar != null) {                            mProgressBar.setVisibility(View.VISIBLE);//显示一个等待的ProgressBar--需要在主线程中执行                        }                    }                })                .subscribeOn(AndroidSchedulers.mainThread())                .observeOn(AndroidSchedulers.mainThread())//指定 Subscriber 所运行在的线程。或者叫做事件消费的线程                .subscribe(new Subscriber<Drawable>() {   //3:订阅 //2:观察者                    @Override                    public void onCompleted() {                        if (mProgressBar!=null){                            mProgressBar.setVisibility(View.GONE);                        }                        Logger.d("观察者 onCompleted()");                        Toast.makeText(MainActivity.this, "观察者 onCompleted()", Toast.LENGTH_SHORT).show();                    }                    @Override                    public void onError(Throwable e) {                        if (mProgressBar!=null){                            mProgressBar.setVisibility(View.GONE);                        }                        Logger.d("观察者 onError()");                        Toast.makeText(MainActivity.this, "观察者 onError() " + e.getMessage(), Toast.LENGTH_SHORT).show();                    }                    @Override                    public void onNext(Drawable drawable) {                        Toast.makeText(MainActivity.this, "观察者 onNext()", Toast.LENGTH_SHORT).show();                        Logger.d("观察者 onNext()");                        mImageView.setImageDrawable(drawable);                    }                });


变换

map()变换
private void demo5() {        Observable.from(DataFactory.getData())                .map(new Func1<Student, String>() {                    @Override                    public String call(Student student) {                        return student.name;                    }                })                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new Subscriber<String>() {                    @Override                    public void onCompleted() {                    }                    @Override                    public void onError(Throwable e) {                    }                    @Override                    public void onNext(String s) {                        XgoLog.i("=======观察者: " + s);                        //                        日志//                        03-17 17:47:46.852 13680-13680/? I/xgo: =======观察者: 学生1//                        03-17 17:47:46.852 13680-13680/? I/xgo: =======观察者: 学生2//                        03-17 17:47:46.852 13680-13680/? I/xgo: =======观察者: 学生3                    }                });    }

 private void demo6() {        Observable.from(DataFactory.getData())                .map(new Func1<Student, String>() {                    @Override                    public String call(Student student) {                        return student.name;                    }                })                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new Action1<String>() {                    @Override                    public void call(String s) {                        XgoLog.i("=======观察者: " + s);//                        日志//                        03-17 17:47:46.852 13680-13680/? I/xgo: =======观察者: 学生1//                        03-17 17:47:46.852 13680-13680/? I/xgo: =======观察者: 学生2//                        03-17 17:47:46.852 13680-13680/? I/xgo: =======观察者: 学生3                    }                });    }
demo5()  和demo6() 是一样的。这又可以看到subscribe()支持不完整定义。

flatMap()变换

/**     * demo7()     * (在observable里创建的被观察者是什么或者最终经过map()或者flatMap()变换之后的是什么类型 在观察者里的数据就是什么类型)     * 需要:输出每一个学生所有选修的课程     * 输出每一个学生选修的课程     */    private void demo7(){        Observable.from(DataFactory.getData())                .map(new Func1<Student, ArrayList<Course>>() {                    @Override                    public ArrayList<Course> call(Student student) {                        return student.courses;                    }                })                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new Action1<ArrayList<Course>>() {                    @Override                    public void call(ArrayList<Course> courses) {                        for (int i=0;i<courses.size();i++){                            XgoLog.i("=======观察者: " + courses.get(i).name);//                            03-17 18:22:35.102 15300-15300/? I/xgo: =======观察者: 学生1的课程1//                            03-17 18:22:35.102 15300-15300/? I/xgo: =======观察者: 学生1的课程2//                            03-17 18:22:35.102 15300-15300/? I/xgo: =======观察者: 学生2的课程1//                            03-17 18:22:35.102 15300-15300/? I/xgo: =======观察者: 学生2的课程2//                            03-17 18:22:35.102 15300-15300/? I/xgo: =======观察者: 学生3的课程1//                            03-17 18:22:35.102 15300-15300/? I/xgo: =======观察者: 学生3的课程2                        }                    }                });    }    /**     * demo8()     * 引入flatMap()     * 需要:输出每一个学生所有选修的课程     * 输出每一个学生选修的课程     */    private void demo8(){        Observable.from(DataFactory.getData())                .flatMap(new Func1<Student, Observable<Course>>() {                    @Override                    public Observable<Course> call(Student student) {                        return Observable.from(student.courses);                    }                })                .subscribeOn(Schedulers.io())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new Action1<Course>() {                    @Override                    public void call(Course course) {                        XgoLog.i("=======观察者: " + course.name);//                        03-17 18:30:00.552 16060-16060/? I/xgo: =======观察者: 学生1的课程1//                        03-17 18:30:00.552 16060-16060/? I/xgo: =======观察者: 学生1的课程2//                        03-17 18:30:00.552 16060-16060/? I/xgo: =======观察者: 学生2的课程1//                        03-17 18:30:00.552 16060-16060/? I/xgo: =======观察者: 学生2的课程2//                        03-17 18:30:00.552 16060-16060/? I/xgo: =======观察者: 学生3的课程1//                        03-17 18:30:00.552 16060-16060/? I/xgo: =======观察者: 学生3的课程2                    }                });    }

认真对比demo7()和demo8()发现flatmap 里面是有一个observerable再来一次,感觉也是找到里面的嵌套循环,但是把数据扁平化而非嵌套的形式挖出来的,就好比一个是“人”字型,一个是“一”字型。


使用

和RxBinding一起使用:
/**         * 防抖动         */        RxView.clicks(mButton1)                .throttleFirst(1000, TimeUnit.MILLISECONDS)//500ms,第一次点击后,500ms内点击无效,500ms后点击才会响应                .subscribeOn(AndroidSchedulers.mainThread())                .observeOn(AndroidSchedulers.mainThread())                .subscribe(new Action1<Void>() {                    @Override                    public void call(Void aVoid) {                        XgoLog.i("========  你点的太快了~");                        Toast.makeText(MainActivity.this, "你点的太快了~", Toast.LENGTH_SHORT).show();                    }}                );

和retrofit的结合
/**     * 获取用户评论 v1.2     *     * @param user_id      用户ObjectId     * @param last         获取下一页数据的条件.如果为空,则从第一条开始查询     * @param head         feed流第一条数据条件     * @param status       评论类型     * @param access_token token     */    @GET("gaschat/user/{id}/comments")    Observable<LTUserComents> getUserAllComments(@Path("id") String user_id,                                                 @Query("last") String last,                                                 @Query("head") String head,                                                 @Query("status") String status,                                                 @Query("access_token") String access_token);

 ServiceFactory.creatService(UserService.class)                            .getUserAllComments(GCApp.getUserData().id, last, head, "1", TokenKeyStore.getInstance().getToken())                            .subscribeOn(Schedulers.io())                            .observeOn(AndroidSchedulers.mainThread())                            .subscribe(new Subscriber<LTUserComents>() {                                @Override                                public void onCompleted() {                                }                                @Override                                public void onError(Throwable e) {                                                                    }                                @Override                                public void onNext(LTUserComents ltUserComents) {                                                                   }                            })
公司目前就是这么用的。

当 RxJava 形式的时候,Retrofit 把请求封装进 Observable ,在请求结束后调用 onNext() 或在请求失败后调用 onError()

对比来看, Callback 形式和 Observable 形式长得不太一样,但本质都差不多。但是一旦情况复杂,r结合rxjava会更明了。

比如callback嵌套的时候

public void saveTheCutestCat(String query , final CallBack<Uri> cutestCatCallback ){        apiWrapper.queryCats(query).start(new CallBack<List<Cat>>() {            @Override            public void onSuccess(List<Cat> result) {                Cat cat = findCat(result);                apiWrapper.store(cat).start(new CallBack<Uri>() {                    @Override                    public void onSuccess(Uri result) {                        cutestCatCallback.onSuccess(result);                    }                    @Override                    public void onError(Exception e) {                        cutestCatCallback.onError(e);                    }                });            }            @Override            public void onError(Exception e) {                    cutestCatCallback.onError(e);            }        });    }    private Cat findCat(List<Cat> list) {        return Collections.max(list);    }
如果结合rxjava就可以这样

private ApiWrapper apiWrapper;    public Observable<Uri> saveTheCutestCat(String query) {        return apiWrapper.queryCats(query)                .map(new Func1<List<Cat>, Cat>() {                    @Override                    public Cat call(List<Cat> cats) {                        return findCat(cats);                    }                }).flatMap(new Func1<Cat, Observable<Uri>>() {                    @Override                    public Observable<Uri> call(Cat cat) {                        return apiWrapper.store(cat);                    }                });    }    private Cat findCat(List<Cat> list) {        return Collections.max(list);    }
简单来说就把人字换成了一字。

假设这么一种情况:你的程序取到的 User 并不应该直接显示,而是需要先与数据库中的数据进行比对和修正后再显示。使用Callback 方式大概可以这么写:

getUser(userId, new Callback<User>() {    @Override    public void success(User user) {        processUser(user); // 尝试修正 User 数据        userView.setUser(user);    }    @Override    public void failure(RetrofitError error) {        // Error handling        ...    }};

但这样做会影响性能。数据库的操作很重,一次读写操作花费 10~20ms 是很常见的,这样的耗时很容易造成界面的卡顿。所以通常情况下,如果可以的话一定要避免在主线程中处理数据库。所以为了提升性能,这段代码可以优化一下:

getUser(userId, new Callback<User>() {    @Override    public void success(User user) {        new Thread() {            @Override            public void run() {                processUser(user); // 尝试修正 User 数据                runOnUiThread(new Runnable() { // 切回 UI 线程                    @Override                    public void run() {                        userView.setUser(user);                    }                });            }).start();    }    @Override    public void failure(RetrofitError error) {        // Error handling        ...    }};

但是各种缩进,如果用 RxJava 的形式就是这样

getUser(userId)    .doOnNext(new Action1<User>() {        @Override        public void call(User user) {            processUser(user);        })    .observeOn(AndroidSchedulers.mainThread())    .subscribe(new Observer<User>() {        @Override        public void onNext(User user) {            userView.setUser(user);        }        @Override        public void onCompleted() {        }        @Override        public void onError(Throwable error) {            // Error handling            ...        }    });

也是变成一字型。













2 0
原创粉丝点击