×

项目需求讨论-RxBus来进行通信

96
青蛙要fly
2017.01.06 11:00* 字数 1939

上一篇文章项目需求讨论-Android推送及Activity启动方式提到一个需求:
当用户是把软件关掉的状态下接受到信息是没关系,反正点击推送信息,都会再打开应用;但是当用户处于上述三个界面的某个界面的时候,都需要立即刷新当前所属界面的信息。比如当前客户是在首页的话,那信息推送过来后,首页就要被刷新;如果是在列表界面,有信息的话,列表界面就要被刷新;如果在详情界面,那详情界面就要被刷新。

我在项目需求讨论-Android推送及Activity启动方式介绍的时候是用startActivity的方式再重新启动当前显示界面的Activity,然后设置Activity的启动方式,然后会调用Activity的onNewIntent方法,所以我们只要在onNewIntent方法中写好相应处理方法即可。

----------------------------------新方法分割线-------------------------------

其实我们可以使用RxBus来进行处理。
(Rxjava基础会的同学看下。不会的请先补下RxJava基础)。

我们知道Rxjava中分为观察者和被观察者,我举个例子,比如一个水龙头,一个拿着杯子的很渴的人,整个事件是当水龙头有水出来的时候,人马上拿着杯子去接住水。当水龙头没水的时候,人就不用去接,在旁边等着就可以。

这里观察者是拿着杯子的人,被观察者是那个水龙头。人会根据水龙头不同的情况从而做出不同的回应。

所以我们在项目中需要类似这样,先建立这三个界面先订阅推送消息,这里三个界面里面的处理类变成了观察者,而收到推送消息的BroadCastReceiver里面的处理类是个被观察者。所以当收到推送消息后,BroadCastReceiver里面的处理类向三个界面里面的处理类发送了通知,告诉他们我收到消息了。然后三个界面的处理类再做相应的处理。

那我们就一步步来,我们这里就用二个Activity来模拟一下。第一个Activity用来代替BroadCastReceiver。里面有个按钮,推送消息发送过来触发改为点击按钮(这个Activity我们假设是A)。然后另外一个Activity就模拟为另外三个Activity中的任意一个(这个Activity我们假设是B)。

----------------------------------------------正式起航--------------------------------------------

我们的整个流程是这样的。启动页面的时候是B页面。只是“显示数据”几个字。


设置TextView点击事件,可以跳转到A页面。这时候我们的B页面就存在了,但是不可见。


我们在A界面有个按钮。点击触发事件,从而通知B界面的数据进行更改。我们按手机的返回按钮返回到B界面。可以看到TextView被更新了。

我们一步步来分析:

1.B界面的TextView被更新,肯定是被通知了。叫它更新,说明它观察了某个被观察者,那个被观察者告诉它我的状态发生了变化,你可以执行你相应的代码了。

解说:所以我们在B界面代码里面一定会对这个被观察者进行观察,知道RxJava基础的朋友一定知道。去订阅(观察)被观察者。是Observable.subscribe(.....)所以我们在B界面一定会获取到某个Observable对象,然后再去执行subscribe(.....)方法。然后里面写上自己的逻辑代码。

2.现在问题变成了我们要生成这么一个Observable来让B界面来观察。生成Observable的代码也很简单,学过Rxjava的同学也应该知道。普通的Observable创建是这样的:

Observable.create(new Observable.OnSubscribe<Integer>() {
            @Override
            public void call(Subscriber<? super Integer> observer) {
                try {
                    if (!observer.isUnsubscribed()) {
                        for (int i = 1; i < 5; i++) {
                            observer.onNext(i);
                        }
                        observer.onCompleted();
                    }
                } catch (Exception e) {
                    observer.onError(e);
                }
            }
        } );

这是一个发送一串数字 :1,2,3,4
然后内部会获取订阅者Subscriber对象,然后调用onNext方法。但是我们是要手动点击了,才去通知那些观察者,我的数据发生变化。所以这时候我们这个观察者又有点特殊了。

整个事件我们要分成二步来看:
1.用户点击按钮,被观察者得知自己的状态被改变了
2.被观察者得知自己状态被改变后,去通知那些观察者。

所以在第一步的时候。这个被观察者本身就去监听了用户点击按钮这个事件。所以在这里这个被观察者本身就又充当了观察者,来观察用户的点击按钮事件。

第二步的时候,这个被观察者才开始了自己的本职工作,让别的观察者来观察自己。

结论:所以我们这里的所需要的这个被观察者就需要具有充当观察者被观察者的功能。

而Rxjava中正好也有这么个类具有该功能:

Subject

Subject可以看成是一个桥梁或者代理,在某些ReactiveX实现中(如RxJava),它同时充当了Observer和Observable的角色。因为它是一个Observer,它可以订阅一个或多个Observable;又因为它是一个Observable,它可以转发它收到(Observe)的数据,也可以发射新的数据。

Subject的种类:
针对不同的场景一共有四种类型的Subject。他们并不是在所有的实现中全部都存在,而且一些实现使用其它的命名约定(例如,在RxScala中Subject被称作PublishSubject)


我们这里使用的是PublishSubject ,下面是PublishSubject的介绍:

PublishSubject

PublishSubject只会把在订阅发生的时间点之后来自原始Observable的数据发射给观察者。需要注意的是,PublishSubject可能会一创建完成就立刻开始发射数据(除非你可以阻止它发生),因此这里有一个风险:在Subject被创建后到有观察者订阅它之前这个时间段内,一个或多个数据可能会丢失。如果要确保来自原始Observable的所有数据都被分发,你需要这样做:或者使用Create创建那个Observable以便手动给它引入"冷"Observable的行为(当所有观察者都已经订阅时才开始发射数据),或者改用ReplaySubject。

如果原始的Observable因为发生了一个错误而终止,PublishSubject将不会发射任何数据,只是简单的向前传递这个错误通知。

-------------------------------------------我是但是君,我是but君-------------------------

所以我们的这个被观察者的创建就是 PublishSubject.create();对于这个Demo中的需求我们可以解决了,但是因为考虑到有时候用RxBus来进行通信的时候会有多线程来调用它,所以我们这里最好这么先转换成SerializedSubject.下面是介绍:

串行化

如果你把 Subject当作一个 Subscriber使用,注意不要从多个线程中调用它的onNext方法(包括其它的on系列方法),这可能导致同时(非顺序)调用,这会违反Observable协议,给Subject的结果增加了不确定性。要避免此类问题,你可以将 Subject转换为一个 SerializedSubject ,类似于这样:
mySafeSubject = new SerializedSubject( myUnsafeSubject );

--------------------------------------我分割君又出来了------------------------------------
所以我们最终的被观察者是这样定义的new SerializedSubject<>(PublishSubject.create());

--------------------------------------结论君coming.....-----------------------------------

为了在A界面和B界面都能使用这个被观察者,所以我们在公用的地方创建new SerializedSubject<>(PublishSubject.create());然后在A界面就可以使用该被观察者来进行充当观察者,然后再B界面对这个观察者进行订阅观察。

剩下的我就直接上代码了:

公用地方创建代码:
public class RxBusUtil {

    public static volatile RxBusUtil instance;
    public final Subject bus;

    public RxBusUtil() {
        bus = new SerializedSubject<>(PublishSubject.create());
    }

    public static RxBusUtil getInstance() {
        if (instance == null) {
            synchronized (RxBusUtil.class) {
                if (instance == null) {
                    instance = new RxBusUtil();
                }
            }
        }
        return instance;
    }

    public void post(Object o) {
        bus.onNext(o);
    }

   // 根据传递的 eventType 类型返回特定类型(eventType)的 被观察者
   //对于本需求来说,不通过这个来toObservable方法来拿Observable对象也可以,可以直接就使用bus对象使用。
    public <T> Observable<T> toObservable (Class<T> eventType) {
        return bus.ofType(eventType);
    }
}
A界面按钮监听调用:
public class A_Activity extends Activity{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.a_activity);
        final Random random = new Random();
        Button btn = (Button) findViewById(R.id.a_btn);
        btn.setOnClickListener(new Button.OnClickListener() {
            @Override
            public void onClick(View view) {
                RxBusUtil.getInstance().post(new Bean("B_Activity",random.nextInt(10000)));
            }
        });
    }
}

B界面代码:
public class B_Activity extends Activity{
    Subscription sub;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.b_activity);
        final TextView textView = (TextView) findViewById(R.id.b_text);

        sub = RxBusUtil.getInstance().bus.subscribe(new Action1() {
            @Override
            public void call(Object o) {
                textView.setText(((Bean) o).getNum()+"");
            }
        });


        textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(B_Activity.this,A_Activity.class));
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if(sub != null){
            sub.unsubscribe();
        }
    }
}

当然什么fragment和Activity之间的也都可以用这种方法来进行通信。

日记本
Web note ad 1