Android之响应式编程RxJava/RxAndroid

RxJava 在 GitHub 主页上的自我介绍是 “a library for composing asynchronous and event-based programs using observable sequences for the Java VM”(一个在 Java VM 上使用可观测的序列来组成异步的基于事件的程序的库)。

特点:简洁
RJava能办得到的事情,用handler+Thread或者AsyncTask都可以做到,而使用Rxjava能使异步操作代码更简洁。随着程序逻辑变得越来越复杂,它依然能够保持简洁。

因为使用一般的异步嵌套,会形成复杂的难易维护的代码。而RxJava是一条从上到下的链式调用,无论多复杂得逻辑都可以用一条线表达,没有嵌套。

友情提醒:看的过程中一定不要眨眼,看清楚这几个字眼,很容易乱,不要搞错。
Observable被观察者
Observer观察者
Subscriber观察者
subscribe订阅
OnSubscribe被观察者内部成员
observeOn线程调度方法
subscribeOn线程调度方法

下面开始RxJava的学习:

首先是引入依赖:

compile 'io.reactivex:rxjava:1.0.14' 
compile 'io.reactivex:rxandroid:1.0.1' 

RxJava是基于扩展的观察者模式实现的。

用我们最熟悉的按钮+监听器来重现观察者模式:

①Button —>按钮,也称被观察者
②OnclickListener —>监听者,也称观察者
③setOnClickListener() —>设置监听者,也称订阅
④onClick() —>点击事件,也称事件

说白了也就是:Button对象通过setOnClickListener(listener)持有OnClickListener对象引用,并且调用OnClickListener对象的回调方法onClick()。那也就是被观察者调用观察者的回调方法,实现了被观察者观察者的事件传递,这就是观察者模式。

在RxJava中,也有四个概念:
①Observable —>被观察者
②Observer/Subscriber —>观察者
③subscribe() —>订阅
④onNext()/onError()/onCompleted()—>事件

  • 与传统观察者模式不同, RxJava 的事件回调方法除了普通事件 onNext() (相当于 onClick() /
    onEvent())之外,还定义了两个特殊的事件:onCompleted() 和 onError()。
  • onCompleted(): 事件队列完结。RxJava 不仅把每个事件单独处理,还会把它们看做一个队列。RxJava
    规定,当不会再有新的 onNext() 发出时,需要触发 onCompleted() 方法作为标志。
  • onError(): 事件队列异常。在事件处理过程中出异常时,onError() 会被触发,同时队列自动终止,不允许再有事件发出。

在一个正确运行的事件序列中, onCompleted() 和 onError() 有且只有一个,并且是事件序列中的最后一个。需要注意的是,onCompleted() 和 onError() 二者也是互斥的,即在队列中调用了其中一个,就不应该再调用另一个。

下面开始来讲如何实现:

一.观察者Observer/Subscriber的创建

Observer<String> observer = new Observer<String>() {
    @Override
    public void onNext(String s) {
        Log.d(tag, "Item: " + s);
    }
    @Override
    public void onCompleted() {
        Log.d(tag, "Completed!");
    }
    @Override
    public void onError(Throwable e) {
        Log.d(tag, "Error!");
    }
};  
Subscriber<String> subscriber = new Subscriber<String>() {
    @Override
    public void onNext(String s) {
        Log.d(tag, "Item: " + s);
    }
    @Override
    public void onCompleted() {
        Log.d(tag, "Completed!");
    }
    @Override
    public void onError(Throwable e) {
        Log.d(tag, "Error!");
    }
};

Observer是接口,Subscriber是实现了Observer接口的一个抽象类,两者的使用方式一模一样,而Subscriber做了一些扩展,而实质上,Observer是会转成一个Subscriber后再使用的。

Subscriber与Observer的区别(Subscriber多出两个方法):
①onStart():事件还未发送之前被调用,如果对准备工作的线程有要求, onStart() 就不适用了,因为它总是在 subscribe 所发生的线程被调用,而不能指定线程。要在指定的线程来做准备工作,可以使用 doOnSubscribe() 方法。

②unsubscribe():用于取消订阅,调用后,Subscriber 将不再接收事件。一般在这个方法调用前,可以使用 isUnsubscribed() 先判断一下状态。

二:被观察者Observable的创建
①create()

Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
    @Override
    public void call(Subscriber<? super String> subscriber) {
        subscriber.onNext("Hello");
        subscriber.onNext("Hi");
        subscriber.onNext("Aloha");
        subscriber.onCompleted();
    }
});

可以看到这里创建Observable的时候,传入了一个OnSubscriber对象,它被生成的Observable所持有,当Observable被订阅subscribe(subscriber)的时候,会调用OnSubscriber.call(subscriber)方法。也就是说订阅的时候,subscribe(subscriber)传入的subscriber对象参数,会从方法OnSubscriber.call(subscriber)传进去执行。那也就是被观察者调用观察者的回调方法,实现了被观察者观察者的事件传递,这就是观察者模式。

②just(T…):将传入的参数依次发出来。

Observable observable = Observable.just("Hello", "Hi", "Aloha");

③from(T[]) 或者from(Iterable):将传入的数组或者Iterable拆分成具体对象,依次发出。

String[] words = {"Hello", "Hi", "Aloha"};
Observable observable = Observable.from(words);

可以看到just,from方法比较简洁,其实这两个方法都是基于create()方法的,也就是说,最终还是调用了create()生成的Observable对象。
这三个方法生成的Observable是完完全全等价的,当Observable对象被订阅subscribe()的时候,将会依次调用:

onNext("Hello");
onNext("Hi");
onNext("Aloha");
onCompleted();

其实还有很多其他的生成Obervable的方法,这里先不列举。

三:subscribe()订阅

订阅代码非常简单

observable.subscribe(observer);
// 或者:
observable.subscribe(subscriber);

我们可以看看subscribe(subscriber)的核心代码:

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

①subscriber首先会执行onStart()方法,这个在前面有提及到。
②执行OnSubscriber.call(subscriber)。
我们在create()的时候,就知道call调用的是这样的代码:

Observable observable = Observable.create(new Observable.OnSubscribe<String>() {
    @Override
    public void call(Subscriber<? super String> subscriber) {
        subscriber.onNext("Hello");
        subscriber.onNext("Hi");
        subscriber.onNext("Aloha");
        subscriber.onCompleted();
    }
});

也就是subscriber会在Observable里面被回调onNext()事件。
③返回Subscription ,为了方便unsubscribe()。

其实你会发现,真正的被观察者不是Observable,而是Observable.OnSubscribe对象。而OnSubscribe是Observable的一个成员对象。

subscribe() 还支持不完整定义的回调:

Action1<String> onNextAction = new Action1<String>() {
    // onNext()
    @Override
    public void call(String s) {
        Log.d(tag, s);
    }
};
Action1<Throwable> onErrorAction = new Action1<Throwable>() {
    // onError()
    @Override
    public void call(Throwable throwable) {
        // Error handling
    }
};
Action0 onCompletedAction = new Action0() {
    // onCompleted()
    @Override
    public void call() {
        Log.d(tag, "completed");
    }
};

// 自动创建 Subscriber ,并使用 onNextAction 来定义 onNext()
observable.subscribe(onNextAction);
// 自动创建 Subscriber ,并使用 onNextAction 和 onErrorAction 来定义 onNext() 和 onError()
observable.subscribe(onNextAction, onErrorAction);
// 自动创建 Subscriber ,并使用 onNextAction、 onErrorAction 和 onCompletedAction 来定义 onNext()、 onError() 和 onCompleted()
observable.subscribe(onNextAction, onErrorAction, onCompletedAction);

Action0 是 RxJava 的一个接口,它只有一个方法 call(),这个方法是无参无返回值的;
Action1是 RxJava 的一个接口,它只有一个方法 call(T param),这个方法也无返回值,但有一个参数;
由于 onCompleted() 方法是无参无返回值的,因此 可以用Action0 可以包装。
由于 onNext(T obj) 和 onError(Throwable error) 也是单参数无返回值的,因此可以用Action1包装。

这里给一个真是的例子

String[] names = ...;
Observable.from(names)
    .subscribe(new Action1<String>() {
        @Override
        public void call(String name) {
            Log.d(tag, name);
        }
    });

非常简单,会依次打印出这个names数组。

四:Scheduler线程调度器

在 RxJava 的默认规则中,事件的发出和消费都是在同一个线程的。而观察者模式本身的目的就是『后台处理,前台回调』的异步机制。

RxJava内置的Scheduler:
①Scheduler.immediate() :当前线程,相当于不指定线程。(默认)
②Scheduler.newThread():启动新线程。
③Scheduler.io():I/O操作所使用的scheduler。内部实现用了一个无上限的线程池,会重用空闲线程,比newThread()效率高。
④Scheduler.computation():CPU密集计算使用的scheduler。
⑤AndroidSchedulers.mainThread():Android主线程scheduler。

使用subscriberOn()和observeOn对线程控制:
1.subscribeOn()
指定 subscribe() 所发生的线程,即 Observable.OnSubscribe 被激活时所处的线程(因为发生subscribe()订阅的时候,会调用Observable.OnSubscribe的call方法)。或者叫做事件产生的线程。

2.observeOn()
指定 Subscriber 所运行的线程。或者叫做事件消费的线程。并且observeOn() 指定的是它之后的操作所在的线程。

不同于 observeOn() , subscribeOn() 的位置放在哪里都可以,但它是只能调用一次的。如果多次调用 subscribeOn() ,只有第一次起作用,后面的都无效。observeOn()可以多次调用。

int drawableRes = ...;
ImageView imageView = ...;
Observable.create(new OnSubscribe<Drawable>() {
    @Override
    public void call(Subscriber<? super Drawable> subscriber) {
        Drawable drawable = getTheme().getDrawable(drawableRes));
        subscriber.onNext(drawable);
        subscriber.onCompleted();
    }
})
.subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程
.observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程
.subscribe(new Observer<Drawable>() {
    @Override
    public void onNext(Drawable drawable) {
        imageView.setImageDrawable(drawable);
    }

    @Override
    public void onCompleted() {
    }

    @Override
    public void onError(Throwable e) {
        Toast.makeText(activity, "Error!", Toast.LENGTH_SHORT).show();
    }
});

RxJava多次切换线程栗子:

Observable.just(1, 2, 3, 4) // 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() 指定

至于为什么可以多次切换线程,在后面讲解了lift()变换原理之后,再做解释。

这样就实现了:加载图片将会发生在 IO 线程,而设置图片则被设定在了主线程。

五:变换
将事件序列中的对象或整个序列进行加工处理,转换成不同的事件或事件序列。
①map(一对一的转换)

Observable.just("images/logo.png") // 输入类型 String
    .map(new Func1<String, Bitmap>() {
        @Override
        public Bitmap call(String filePath) { // 参数类型 String
            return getBitmapFromPath(filePath); // 返回类型 Bitmap
        }
    })
    .subscribe(new Action1<Bitmap>() {
        @Override
        public void call(Bitmap bitmap) { // 参数类型 Bitmap
            showBitmap(bitmap);
        }
    });

Func1 和 Action1 非常相似,是 RxJava 的一个接口,用于包装含有一个参数的方法。 Func1 和 Action 的区别在于, Func1 包装的是有返回值的方法。

map() 方法将参数中的 String 对象转换成一个 Bitmap 对象后返回,而在经过 map() 方法后,事件的参数类型也由 String 转为了 Bitmap。

相当于:
Observable.just(“images/logo.png”)
转换成
Observable.just(bitmap)

②flatMap(一对多的转换)

Student[] students = ...;
Subscriber<Course> subscriber = new Subscriber<Course>() {
    @Override
    public void onNext(Course course) {
        Log.d(tag, course.getName());
    }
    ...
};
Observable.from(students)
    .flatMap(new Func1<Student, Observable<Course>>() {
        @Override
        public Observable<Course> call(Student student) {
            return Observable.from(student.getCourses());
        }
    })
    .subscribe(subscriber);

flatMap() 的原理是这样的:
1. 使用传入的事件对象创建一个 Observable 对象;
2. 并不发送这个 Observable, 而是将它激活,于是它开始发送事件;
3. 每一个创建出来的 Observable 发送的事件,都被汇入同一个 Observable ,而这个 Observable 负责将这些事件统一交给 Subscriber 的回调方法。

对于上面的例子,目的是把每一个学生的课程打印出来(一个学生有多个课程)。
可以看到flatMap方法使用传入Student事件对象,创建并返回Observable对象。多次执行flatMap后会返回很多个Observable,最后每一个Observable都会被汇入到同一个新的Observable中,而这个 新的Observable 负责将这些事件统一交给 Subscriber 的回调方法;




…剩下的很多常用的操作符,以后再做补充…

传统嵌套网络请求,用rxjava可以链式表达:rxjava+retrofit

networkClient.token() // 返回 Observable<String>,在订阅时请求 token,并在响应后发送 token
    .flatMap(new Func1<String, Observable<Messages>>() {
        @Override
        public Observable<Messages> call(String token) {
            // 返回 Observable<Messages>,在订阅时请求消息列表,并在响应后发送请求到的消息列表
            return networkClient.messages();
        }
    })
    .subscribe(new Action1<Messages>() {
        @Override
        public void call(Messages messages) {
            // 处理显示消息列表
            showMessages(messages);
        }
    });

六:变换原理 lift()
这些变换虽然功能各有不同,但实质上都是针对事件序列的处理和再发送。类型转换都是通过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);//传进来的subscriber会被进行一轮包装(变换)成newSubscriber 
            newSubscriber.onStart();
            onSubscribe.call(newSubscriber);//这个onSubscribe指的是原始Observable内的那个onSubscribe,不属于当前正在创建的这个Observable。
        }
    });
}

可以看到lift()方法,传入一个Operator参数,返回一个Observable对象;这个返回的Observable对象,是通过create生成的。

原始有一个Observable和一个OnSubscriber,经过lift()以后,又产生了一个新的Observable和一个新的OnSubscriber。那么新的Observable被订阅的时候,就会触发新的OnSubscriber的call()方法,也就是上面代码那一个call方法。在call方法里面传入的subscriber会被变换包装成新的subscriber,紧接着先调用新的subscriber的onStart()方法,接着调用原始onSubscribe的call方法,在里面正式调用新subscriber的回调方法。也就是说,新的Observable仅仅起一个代理的功能,最终的事件分发,还是在原始Observable中。(只不过,在开发中,我们完全可以理解一个Observable被lift()后依旧是一个简单的仅仅事件类型变换了的Observable)

举一个栗子:
将事件中的 Integer 对象转换成 String

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);
            }
        };
    }
});

以上代码中Operator的call方法的调用,是在lift()调用产生新的Observable被订阅的时候,触发新的OnSubscriber.call(subscriber)方法里,对subscriber进行变换和包装的方法。

这一段代码的原意是要把Observable<Integer>转换成Observable<String>,表面上我们看到的,也以为是Observable<Integer>被转换成了Observable<String>,以为里面的数据类型从Integer变成了String。

真相是:
原始Observable<Integer>没变,然后生成了新的Observable<String>并返回,我们手中持有的是新的这个Observable<String>,而这个新的Observable<String>能够接收String类型的subscriber<String>的订阅,而订阅触发的新的OnSubscriber调用call方法,在里面对subscriber<String>进行转换包装成subscriber<Integer>,接着用原始的OnSubscriber调用call,把这个包装后的subscriber<Integer>进行事件分发(调用onNext()..onCompleted()),因为原始Observable<Integer>是Integer类型,它能够处理这个类型的subscriber<Integer>。那么在我们眼中,就好像是把事件参数类型Integer转变成了String,而事实是subscriber的类型做了变换。这些新的Observable其实都起了一个代理的作用,subscriber仅仅是经过了他们,最后都会被传到最原始的observable里面,被它执行(回调)。这就给了我们一个假象。

七:线程多次切换原理

RaJava是可以多次切换线程的,为什么?

因为observeOn()方法指定的是Subscriber观察者的线程,然而这个Subscriber并不是subscribe(Subscriber)订阅传入的这个Subscriber,而是observeOn执行时那个Observable所对应的Subscriber。(根据lift()变换原理,我们知道,rxjava链式每次变换操作后,Observable对象会变化,那么它持有Subscriber对象也会变化(由下级包装上传过来的),是经过转换包装的,这个Subscriber也就是通过当前Observable对象的onSubscriber对象调用call(Subscriber)方法传入的Subscriber对象)

observeOn() 指定的是它之后的操作所在的线程。因此如果有多次切换线程的需求,只要在每个想要切换线程的位置调用一次 observeOn() 即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值