Android中响应式编程--Subject的使用

Subject是RxJava中的一个类,既可以作为观察者,也可以作为被观察者,可以用来做响应式编程。

Subject主要分为PublishSubject、ReplaySubject和BehaviorSubject三种,特性如下:

    PublishSubject:广播Subject,向所有接收者发送事件,事件不存入缓存区

    ReplaySubject:发送缓存区中最后几个事件,事件数量可以通过构造方法传进去

    BehaviorSubject:发送缓存区中最后一个事件


下面,我通过一个例子,向大家演示三者的用法


ReactiveList类

在这个类里面,我们定义三个Subject和三个Observer,Observer用于监听事件的触发,然后把事件传递到Subject中,而后Subject再处理事件,完了在onSubscribe()中反馈结果。


1、定义ReactiveList类(单例,此处略过),使用泛型。在里面定义枚举类型,表示改变的类型(增删改)

public enum ChangeType {
    ADD, REMOVE, UPDATE
}


2、定义一个LinkedList,做为数据源,一切改变由它而起;而后,定义三个Subject,分别表示改变种类(哪种改变)、改变的对象、最近改变的对象。Subject要指定两个泛型,前者是观察者类型,后者是被观察者类型,这里我们设置成一样的。

private LinkedList<T> list = new LinkedList<T>();
private Subject<ChangeType,ChangeType> changes = null;//改变种类
private Subject<T,T> changeValues = null;//改变的对象
private Subject<T,T> latestChanged = null;//最近的改变对象


3、再定义三个Observer,用于监听事件的触发。

private Observer<T> addObserver = null;
private Observer<T> removeObserver = null;
private Observer<T> updateObserver = null;


4、ReactList的构造方法,先初始化三个Subject:

changes = PublishSubject.<ChangeType>create().toSerialized();
changeValues = ReplaySubject.<T>createWithSize(5).toSerialized();
latestChanged = BehaviorSubject.<T>create().toSerialized();

    其中:toSerialized()方法用来保证线程安全

             ReplaySubject的createWithSize()就是定义取出缓冲区最后多少个事件


5、紧接着,初始化三个Observer:

addObserver = new SerializedObserver<>(new Observer<T>() {
    @Override
    public void onCompleted() {
        changes.onCompleted();
        changeValues.onCompleted();
    }

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

    @Override
    public void onNext(T t) {
        add(t);
    }
});

removeObserver = new SerializedObserver<>(new Observer<T>() {
    @Override
    public void onCompleted() {
        changes.onCompleted();
        changeValues.onCompleted();
    }

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

    @Override
    public void onNext(T t) {
        delete(t);
    }
});

updateObserver = new SerializedObserver<>(new Observer<T>() {
    @Override
    public void onCompleted() {
        changes.onCompleted();
        changeValues.onCompleted();
    }

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

    @Override
    public void onNext(T t) {
        update(t);
    }
});

    其中为了线程安全,也使用了SerializedObserver,而在onNext()方法中,我是调用了自己封装的add、remove和update方法,它们仨的实现也很简单:

private void add(T value){
    list.add(value);
    changes.onNext(ChangeType.ADD);
    changeValues.onNext(value);
    latestChanged.onNext(value);
}

private void delete(T value){
    if(list.contains(value)){
        list.remove(value);
        changes.onNext(ChangeType.REMOVE);
        changeValues.onNext(value);
        latestChanged.onNext(value);
    }
}

private void update(T value){
    if(list.contains(value)) {
        int index = list.indexOf(value);
        list.set(index,value);
        changes.onNext(ChangeType.UPDATE);
        changeValues.onNext(value);
        latestChanged.onNext(value);
    }
}

    主要就是调用三个Subject的onNext()方法,此刻,Subject是做为被观察者


6、而后,向外界提供三个Subject和三个Observer的调用接口(六个get方法)

public Subject<ChangeType,ChangeType> changes(){
    return changes;
}

public Subject<T,T> changesValues(){
    return changeValues;
}

public Observable<T> latestChanged(){
    return latestChanged;
}

public Observer<T> adder(){
    return addObserver;
}

public Observer<T> remover(){
    return removeObserver;
}

public Observer<T> updater(){
    return updateObserver;
}


7、最后,给我们的list也写一个get方法,不过这个get方法,要返回一个被观察者

public Observable<T> list(){
    LinkedList<T> copy = new LinkedList<T>();
    synchronized (copy){
        copy.addAll(list);
    }
    return Observable.from(copy);
    //创建操作符,创建一个被观察者,就自动发送事件
}


初始化数据

ReactList类构造完后,我们就可以在MainActivity中初始化数据了,写一个Student数组(Student就是java bean类,属性是id+name,无参+带参构造方法+get/set+toString())

private Student[] students = new Student[]{
        new Student("001", "杰森伯恩"),
        new Student("002", "托马斯穆勒"),
        new Student("003", "莱万多夫斯基"),
        new Student("004", "阿尔杰罗本"),
        new Student("005", "弗兰克兰帕德"),
        new Student("006", "史蒂文杰拉德"),
        new Student("007", "诺伊尔"),
        new Student("008", "罗伯特卡洛斯"),
        new Student("009", "因扎吉"),
        new Student("010", "里奥费迪南德")
};


测试三个Subject的特性

根据前文我们知道,PublishSubject是向所有订阅者发送时间,不经过缓存区,而ReplaySubject和BehaviorSubject,它俩发送事件都是先放到缓存区里,所以我们在按顺序订阅Publish、Replay、Behavior的情况下,分三次实验:

        第一次:在PublishSubject订阅之前,发送数据

        第二次:在PublishSubject订阅之后,ReplaySubject订阅之前,发送数据

        第三次:在三个Subject订阅之后,发送数据

我们把发送数据(按增、改、删顺序)统一封装成一个方法,名曰dataOperation():

private void dataOperation() {
    for (int i = 0; i < students.length; i++) {
        reactiveList.adder().onNext(students[i]);
    }

    reactiveList.list().subscribe(student -> Log.i(TAG, "list:" + student.toString()));

    students[5].setName("卡尼吉亚");
    reactiveList.updater().onNext(students[5]);

    reactiveList.remover().onNext(students[0]);
}

而后,定义静态变量count,记录changeType的变化次数,进行三次实验:


第一次实验
/**
 * 这种情况:先发事件,再订阅
 * 对于Replay和Behavior,由于发事件的时候没有订阅者,事件全部进入缓存区。
 * 而对于PublishSubject,它的事件由于不存入缓存区,所以都流失了。
 * 最终结果:Replay和Behavior正常,Publish没有输出任何数据
 *
 * 此处,订阅者和subject就是观察者,我们(用户)、observer和subject则是被观察者,
 * 因为我们调用了观察者observer的onNext()方法,observer调用了subject的onNext()方法,subject调用了订阅者的call()方法
 *
 * 所以这个案例的观察链就是:
 *            用户->observer->subject->订阅者
 *
 */
dataOperation();

reactiveList.changes().subscribe(changeType -> Log.i(TAG, "changeType-"+(++count)+":" + changeType));

reactiveList.changesValues().subscribe(student -> Log.i(TAG, "changesValues:" + student.toString()));

reactiveList.latestChanged().subscribe(student -> Log.i(TAG, "lastedChanged:" + student.toString()));


不多解释了,注释写的很清楚,我们看一下输出日志


可以看到,changeType(也就是Publish)的事件全部流失,而changesValues(Replay)和lastedChanged(Behavior)分别输出了最后五个事件(三个增加、一个更改,一个删除)和最后一个事件(删除)


第二次实验
reactiveList.changes().subscribe(changeType -> Log.i(TAG, "changeType-"+(++count)+":" + changeType));


/**
 * 这种情况:先订阅Publish,再发数据,最后订阅Replay和Behavior:
 * 由于发事件的时候,对于Replay和Behavior,情况和第一种一样,所以依旧正常
 * 而对于Publish,由于是先订阅,所以数据发一次,订阅者就接收一次,故而正常输出
 */
dataOperation();

reactiveList.changesValues().subscribe(student -> Log.i(TAG, "changesValues:" + student.toString()));

reactiveList.latestChanged().subscribe(student -> Log.i(TAG, "lastedChanged:" + student.toString()));


而后,直接看日志


可以看到changeType(Publish)也正常输出了


第三次实验
reactiveList.changes().subscribe(changeType -> Log.i(TAG, "changeType-"+(++count)+":" + changeType));

reactiveList.changesValues().subscribe(student -> Log.i(TAG, "changesValues:" + student.toString()));

reactiveList.latestChanged().subscribe(student -> Log.i(TAG, "lastedChanged:" + student.toString()));

/**
 * 这种情况:先订阅,再发事件
 * 对于Replay,Behavior和Publish,由于发来一个事件就直接有订阅者接收,就不存在缓冲区的问题了
 * 所以结果是发一次事件,三个都按接收顺序各输出一次
 */
dataOperation();

日志如下:


果然,是按着订阅顺序,发一次事件打印一次日志(包括lastedChanged(Behavior))。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值