RxJava学习笔记

#RxJava学习笔记
本文旨在梳理下RxJava的相关知识,所谓好记性不如烂笔头,方便日后有需要可以迅速查阅,像大多数学习RxJava的童鞋一样,推荐 给 Android 开发者的 RxJava 详解 一文,相信这篇文章对于需要入门的你再合适不过。

本文目录如下:

RxJava简介

RxJava is a Java VM implementation of Reactive Extensions: a library for composing asynchronous and event-based programs by using observable sequences. —— GitHub上RxJava自我介绍

“异步"以及"基于事件”,正是对RxJava的高度概括,这两个属性带来了RxJava 简洁+异步的优势,也是目前RxJava如此热门的原因所在。

基础概念

说到RxJava,不得不提的就是观察者模式,Android中常用的点击事件,ListView更新,都是观察者模式,大概模式如下:

Button->onClick(View)->OnClickListener
ListView Data Update -> notify -> Adapter

RxJava基于观察者模式, 有如下基本概念:

  1. Observable (可观察者,即被观察者)
  2. Observer (观察者)
  3. subscribe (订阅)、事件

Observable 和 Observer 通过 subscribe() 方法实现订阅关系,从而 Observable 可以在需要的时候发出事件来通知 Observer。支持onNext() ,onCompleted() 和 onError()等回调方法

大概模式如下图:
RxJava大概模式

RxJava基础使用

  1. 定义Subscriber/Observer
    Subscriber/Observer 即观察者,它决定事件触发的时候将有怎样的行为。
Subscriber<String> mySubscriber= new Subscriber<String>() {
        @Override
        public void onCompleted() {
            Log.d(TAG,"onCompleted");
        }
        @Override
        public void onError(Throwable throwable) {
            Log.e(TAG,"onError"+throwable.toString());
        }
        @Override
        public void onNext(String s) {
            Log.d(TAG,"onNext,s = " + s);
        }
    }

也可以用Observer,Observer相对Subscriber少了onStart()和unsubscribe()两种方法

2.定义Observable
Observable,它决定什么时候触发事件以及触发怎样的事件。

Observable myObservable = Observable.create(new Observable.OnSubscribe<String>() {
        @Override
        public void call(Subscriber<? super String> subscriber) {
            subscriber.onNext("welcome to");
            subscriber.onNext("Saberhao's");
            subscriber.onNext("Blog");
            subscriber.onCompleted();
        }
    });

当然,也可以使用just, from等关键字实现相同的功能

Observable<String> myObservable = myObservable.just("welcome to","Saberhao's","Blog")

或者

String[] words = {"welcome to","Saberhao's","Blog"};
Observable myObservable = myObservable .from(words);

3 订阅

 mySubscriber.subscribe(myObservable); 

实现的核心代码:

public Subscription subscribe(Subscriber subscriber) {
    subscriber.onStart();
    onSubscribe.call(subscriber);
    return subscriber;
}

可以看到,subscriber() 做了3件事:

  1. 调用 Subscriber.onStart() 。
  2. 调用 Observable 中的OnSubscribe.call(Subscriber) 。在这里,事件发送的逻辑开始运行。从这也可以看出,在 RxJava 中,Observable 并不是在创建的时候就立即开始发送事件,而是在它被订阅的时候,即当 subscribe() 方法执行的时候。
  3. 将传入的 Subscriber 作为 Subscription 返回。这是为了方便 unsubscribe().

一旦订阅激活,就会开始打印字符串,效果如下:

这里写图片描述

Subscribe() 还支持不完整定义的回调,RxJava根据定义创建出Subscriber,上面的等价代码如下

 Action1<String> onNextAction = new Action1<String>() {
            @Override
            public void call(String s) {
                Log.d(TAG, "onNext,s = " + s);
            }
        };
        Action1<Throwable> onErrorAction = new Action1<Throwable>() {
            @Override
            public void call(Throwable throwable) {
                Log.e(TAG, "onError" + throwable.toString());
            }
        };
        Action0 onCompleteAction = new Action0() {
            @Override
            public void call() {
                Log.d(TAG,"onCompleted");
            }
        };

        Observable myObservable = Observable.create(new Observable.OnSubscribe<String>() {
            @Override
            public void call(Subscriber<? super String> subscriber) {
                subscriber.onNext("welcome to");
                subscriber.onNext("Saberhao's");
                subscriber.onNext("Blog");
                subscriber.onCompleted();
            }
        });
        // 自动创建 Subscriber ,并使用 onNextAction 来定义 onNext()
        myObservable.subscribe(onNextAction);
        // 自动创建 Subscriber ,并使用 onNextAction 和 onErrorAction 来定义 onNext() 和 onError()
        myObservable.subscribe(onNextAction, onErrorAction);
        // 自动创建 Subscriber ,并使用 onNextAction、 onErrorAction 和 onCompletedAction 来定义 onNext()、 onError() 和 onCompleted()
        myObservable.subscribe(onNextAction, onErrorAction, onCompleteAction);

ActionX 是 RxJava 的一个接口,它只有一个方法 call(),X代表参数个数,0就是没有参数,1就是一个,以此类推,ActionX相当于一个包装对象,将自己的返回值传入对应的subscriber()方法中。

使得RxJava变得灵活,主要有两点: 变换 以及 线程控制,下面我们先通过介绍操作符来看看RxJava是如何变换的

Operate操作符

map()

map的意思就是"映射",也就是把不同类型,或者同一类型的不同数据进行变换,其实 本质就是数据的处理,嗯,我是这样理解的,举个简单的栗子
假如我有一堆图片的URL, 我现在想通过这些URL获取图片,那么我就可以用到map啦

 void showMapUse(){
        String[] mImageUrls = ...;
        Subscriber<String> subscriber = new Subscriber<String>() {
            @Override
            public void onCompleted() {
                
            }

            ...
        };
        Observable.from(mImageUrls)
                .map(new Func1<String, Bitmap>() {
                    @Override
                    public Bitmap call(String mImageUrl) {
                        return getBitmapFromUrl(mImageUrl);
                    }
                })
                .subscribe(subscriber);
    }

这里就华丽丽的实现了从 Stirng 到 Bitmap的类型转换,其实也就是对URL做了处理
对了,好像有什么漏了,对了 这里用到了Func1,其实Func1,FuncX和ActionX都是RxJava封装好的接口,X代表参数个数,他们的区别就在于FuncX有返回值,而ActionX没有。

flatMap()

flatMap意思就是“铺平”+ “映射”,需要“铺平”很明显就是一个集合,下面我们就举一个关于集合的栗子,来看看flatMap怎么使用。
话说中秋到了,公司要给每个员工发中秋礼品,每个人都可以得到若干份,现在请你帮忙打印大家挑选好的礼物(好吧,这个栗子我是想了好久才想到的,原谅我一开始没铺垫好)
先看不用flatMap的实现:

Staff[] staffs = ...;
        Subscriber<Staff> subscriber = new Subscriber<Staff>() {
            @Override
            public void onNext(Staff staff) {
                List<Gift> gift = staff.getGift();
                for (int i = 0; i < gift.size(); i++) {
                    Course course = gift.get(i);
                    Log.d(TAG, course.gift());
                }
            }
            ...
        };
        Observable.from(staffs)
                .subscribe(subscriber);

这里有什么问题呢,如果员工的礼物需要到数据库提取,那样放到subscriber()中执行就显得不合适,而且这样写代码也不够简洁,来看看我们用flatMap是怎么实现的

Staff[] staffs = ...;
        Subscriber<Gift> subscriber = new Subscriber<Gift>() {
            @Override
            public void onNext(Gift gift) {
                Log.d(TAG, gift.getName());
            }
            ...
        };
        Observable.from(staffs)
                .flatMap(new Func1<Staff, Observable<Gift>>() {
                    @Override
                    public Observable<Gift> call(Staff staff) {
                        return Observable.from(staff.getGift());
                    }
                })
                .subscribe(subscriber);

实际上很好理解,就是把原来staffs的Observable “铺平”成子Observable, 然后把子Observable在进行“映射”,最后交给subscriber同一订阅处理,流程如下图:
这里写图片描述

lift()

无论是Map还是flatMap,本质上都是针对事件序列的处理和再发送,在RxJava中,他们都是基于lift() 变换方法。lift核心代码:

public <R> Observable<R> lift(Operator<? extends R, ? super T> operator) {
    return Observable.create(new OnSubscribe<R>() {
        @Override
        public void call(Subscriber subscriber) {
            Subscriber newSubscriber = operator.call(subscriber);
            newSubscriber.onStart();
            onSubscribe.call(newSubscriber);
        }
    });
}

简单介绍一下,总体上来讲,就是根据operator,通过lift(),来生成一个Observable对象,
这个新的observable对象订阅时,会调用到call方法,call方法主要实现三个功能,也就上面三句代码:

  1. 根据operator 来生成一个新的subscriber
  2. 启动subcriber
  3. 也是最关键的一步,将新的subscriber 传递给新的observable

通过上面三步,就完成了一个订阅事件的封装和传递,总体来说,有点像代理模式,原有的observable以为自己启动了订阅内容,其实他是通过new observable实现的。
流程如下:
这里写图片描述

灵活使用lift(),你就可以定义自己的转换操作咯,下面是从参考文章 摘录来的类型转换的例子,虽然没什么实际意义,不过留下来以后可以参考

observable.lift(new Observable.Operator<String, Integer>() {
    @Override
    public Subscriber<? super Integer> call(final Subscriber<? super String> subscriber) {
        // 将事件序列中的 Integer 对象转换为 String 对象
        return new Subscriber<Integer>() {
            @Override
            public void onNext(Integer integer) {
                subscriber.onNext("" + integer);
            }

            @Override
            public void onCompleted() {
                subscriber.onCompleted();
            }

            @Override
            public void onError(Throwable e) {
                subscriber.onError(e);
            }
        };
    }
});

Compose()

Compose()和 lift() 的区别在于, lift() 是针对事件项和事件序列的(针对内部),而 compose() 是针对 Observable 自身进行变换(针对observable所出环境)。举个例子,(http://gank.io/post/560e15be2dca930e00da1083#toc_22) 假设在程序中有多个 Observable ,并且他们都需要应用一组相同的变换集合。你可以这么写:

private Observable changeAll(Observable observable) {
    return observable
        .map()
        .flatMap()
        .lift();
}
...
changeAll(observable1).subscribe(subscriber1);
changeAll(observable2).subscribe(subscriber2);
changeAll(observable3).subscribe(subscriber3);

这个时候observable 被包含在changeAll方法中,我们要让observable解放出来,让他更符合RxJava的灵活性,可以使用Compose() :

public class ChangeAllTransformer implements Observable.Transformer<Integer, String> {
    @Override
    public Observable<String> call(Observable<Integer> observable) {
        return observable
            .map()
            .flatMap()
            .lift();
    }
}
...
Transformer changeAll = new ChangeAllTransformer();
observable1.compose(changeAllTransformer).subscribe(subscriber1);
observable2.compose(changeAllTransformer).subscribe(subscriber2);
observable3.compose(changeAllTransformer).subscribe(subscriber3);

其他常见操作

比较常用的操作符还有 filter(),take(),doOnNext(), 分别用于过滤,指定输出结果数量,以及在每次输出前做些特定操作。
接着上面月饼的例子,如果月饼是必备的,所以我只查询不是月饼的礼物,且只想知道其中的两种礼物(¯﹃¯),同时我想把这些礼物名单保留下来,我该怎么做?

Staff[] staffs = ...;
Subscriber<Gift> subscriber = new Subscriber<Gift>() {
    @Override
    public void onNext(Gift gift) {
        Log.d(TAG, gift.getName());
    }
    ...
};
Observable.from(staffs)
        .flatMap(new Func1<Staff, Observable<Gift>>() {
            @Override
            public Observable<Gift> call(Staff staff) {
                return Observable.from(staff.getGift());
            }
        })
        .filter((new Func1<gift, Boolean>() {
		   @Override
		   public Boolean call(Gift gift) {
		       return !gift.getName().equals("月饼");
		   }
		})
		.take(2)
		.doOnNext(new Action1<Gift>() {
            @Override
            public void call(Gift gift) {
                saveGiftName(gift.getName);
            }
        });
		.subscribe(subscriber);

doOnNext虽然名字和SubScribe().onNext差不多,但是在线程处理上没必然联系,具体请自行谷歌~ RxJava还有许多操作符,后面会慢慢收集丰富~

线程控制Scheduler

写到这里 ,以及好累了,不分篇就是有这种不好的地方,开篇说过,RxJava的特定就是简洁+异步,RxJava使用调度器Scheduler进行线程切换,极大满足了用户异步操作的需求,真正做到,后台处理数据,前台显示!

基本用法

subscribeOn : 指定订阅响应动作所在线程,只能指定一次
observeOn():指定在它之后的操作所在的线程,可以不断切换

一般的用法:

Observable.from(staffs) // IO 线程,由 subscribeOn() 指定
    .subscribeOn(Schedulers.io())
    .observeOn(Schedulers.newThread())
    .flatMap(flatMapOperator) // 新线程,由 observeOn() 指定
    .observeOn(Schedulers.io())
    .map(mapOperator) // IO 线程,由 observeOn() 指定

线程切换进阶

为啥说subscribeOn只能指定一次,其实很好理解,就是订阅的响应只有一个,所以只认一次切换,当然比较高冷的说法,可以从原理来分析。

subscribeOn()原理:

这里写图片描述

observeOn() 原理:

这里写图片描述

图中可以看出,subscribeOn() 和 observeOn() 都做了线程切换的工作(图中的 “schedule…” 部位,仔细找可以看到,反正我也是找了很久),subscribeOn() 的线程切换发生在 OnSubscribe 中,在没有通知上一级时,这个线程还是有可能被切换。而 observeOn() 的线程切换则发生在它内建的 Subscriber 中,即发生在它即将给下一级 Subscriber 发送事件时,因此 observeOn() 控制的是它后面的线程。

结合下面的图片,就可以很好理解这些概念了:
这里写图片描述

从图我们可以了解到:

  1. subscribeOn只认一次切换
  2. subscribeOn只认第一次切换

防止内存泄露

在 subscribe() 之后, Observable 会持有 Subscriber 的引用,这个引用如果不能及时被释放,将有内存泄露的风险。所以最好保持一个原则:要在不再使用的时候尽快在合适的地方(例如 onPause() onStop() 等方法中)调用 unsubscribe() 来解除引用关系,以避免内存泄露的发生。

Subscription 类只有两个方法,unsubscribe() 和 isUnsubscribed()。通常在需要取消订阅的地方,先查询是否订阅 而后取消订阅,这个步骤是固定的,如果你正在处理多个 Observables 和 Subscribers,可以将他们添加到CompositeSubscription中,,然后可以使用 CompositeSubscription.unsubscribe() 方法在同一时间进行退订(unsubscribed)。

public void onUnsubscribe() {
        if (compositeSubscription != null&&compositeSubscription.hasSubscriptions()) {
            compositeSubscription.unsubscribe();
        }
    }

后记

虽然参考文章写的非常好,但是自己动笔才发现许多模糊的地方,写完本文后理清不少,以后还是勤快点更新博客,第一次用MD写博客,体验还不错~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值