RxJava作为事件总线?

12

我开始学习RxJava,迄今为止我很喜欢它。我有一个与活动通信的片段,点击按钮时(用于替换当前片段为新片段)。Google建议使用接口来使片段向上通信到活动中,但它太冗长了,我尝试使用广播接收器,虽然常规情况下可以工作,但也存在缺点。

由于我正在学习RxJava,我想知道从片段到活动(或从片段到片段)进行通信是否是一个好的选择?如果是,那么使用RxJava进行此类通信的最佳方法是什么?我需要像这个网站那样制作事件总线吗?如果是这种情况,我应该制作总线的单个实例并在全局范围内使用它(使用主题)吗?

3个回答

11

是的,一旦学会了,这很棒。考虑以下单例类:

public class UsernameModel {

    private static UsernameModel instance;

    private PublishSubject<String> subject = PublishSubject.create();

    public static UsernameModel instanceOf() {
        if (instance == null) {
            instance = new UsernameModel();
        }
        return instance;
    }

    /**
     * Pass a String down to event listeners.
     */
    public void setString(String string) {
        subject.onNext(string);
    }

    /**
     * Subscribe to this Observable. On event, do something e.g. replace a fragment
     */
    public Observable<String> getStringObservable() {
        return subject;
    }

}
在您的Activity中,请准备接收事件(例如,在onCreate中添加该功能):
UsernameModel usernameModel = UsernameModel.instanceOf();

//be sure to unsubscribe somewhere when activity is "dying" e.g. onDestroy
subscription = usernameModel.getStringObservable()
        .subscribe(s -> {
            // Do on new string event e.g. replace fragment here
        }, throwable -> {
            // Normally no error will happen here based on this example.
        });
在您的片段中,当事件发生时将其传递下去:
UsernameModel.instanceOf().setString("Nick");

您的活动将会执行某些操作。

提示1:使用任何您喜欢的对象类型来更改字符串。

提示2:如果您有依赖注入,它也可以很好地工作。


更新:我撰写了一篇更详细的文章


1
  1. 你的示例最好使用BehaviorSubject来实现"粘性"观察,新的订阅者将看到最后一个值。你可能希望这样做,因为依赖该值的新视图在订阅时将正确地更新。
  2. UsernameModel只做了一件事:包装了一个PublishSubject并使其成为单例。DI提供了单例!如果您正在使用DI,则可以直接注入PublishSubject或BehaiorSubject。如果您使用的是Dagger,则可以使用"@Named("username")"来注入Subject。
- Andrew Gallasch
类似的实现请查看下面的答案 - Aks4125

3

目前我认为对于这个问题,我更倾向于:

1.) 不使用一个全局总线来处理整个应用程序中的所有内容(可能会变得非常笨拙),而是使用“本地”总线来清晰定义目的,并仅在需要时将其插入。

例如,您可能会有:

  • 一个总线用于在ActivityApiService之间发送数据。
  • 一个总线用于在一个Activity中多个Fragment之间通信。
  • 一个总线,向所有Activity发送当前选择的应用程序主题颜色,以便它们可以相应地调整所有图标的色调。

2.) 使用Dagger(或者如果您更喜欢的话,也可以使用AndroidAnnotations)使连接一切变得不那么痛苦(并且避免了许多static实例)。这也使得更容易,例如:只有一个组件专门处理在SharedPreferences中存储和读取登录状态——然后该组件可以直接与您的ApiService连接,为所有请求提供会话令牌。

3.) 内部可以自由使用Subject,但在通过调用return subject.asObservable()将其传递给公众之前,请将其“转换”为Observable。这可以防止其他类将值推入Subject中,而它们不应该被允许。


1
我真的很好奇,拥有多个事件总线实例的意义是什么?这并没有促进高内聚性。与其拥有一个实例,你最终会得到多个相同的实例,却没有任何真正的好处。 - Gunhan
@Gunhan 不要再四处尝试转换类型了。每个特定的总线都有一个特定的通信模型。如果您需要监听“SomeObject”的更改,为什么要通过同一条总线发送“SomeObjects”,而“SomeOtherObject”也可能在其中传输。具体的总线是专用的多层特定信息通道。 - Martin Marconcini

0
定义事件。
public class Trigger {

public Trigger() {
}

public static class Increment {
}

public static class Decrement {
}

public static class Reset {
}
}

事件控制器
public class RxTrigger {

private PublishSubject<Object> mRxTrigger = PublishSubject.create();

public RxTrigger() {
    // required
}

public void send(Object o) {
    mRxTrigger.onNext(o);
}

public Observable<Object> toObservable() {
    return mRxTrigger;
}
// check for available events
public boolean hasObservers() {
    return mRxTrigger.hasObservers();
}
}

Application.class

public class App extends Application {

private RxTrigger rxTrigger;

public App getApp() {
    return (App) getApplicationContext();
}

@Override
public void onCreate() {
    super.onCreate();
    rxTrigger = new RxTrigger();
}


public RxTrigger reactiveTrigger() {
    return rxTrigger;
}


}

在需要的地方注册事件监听器。
               MyApplication mApp = (App) getApplicationContext();
               mApp
                    .reactiveTrigger() // singleton object of trigger
                    .toObservable()
                    .subscribeOn(Schedulers.io()) // push to io thread
                    .observeOn(AndroidSchedulers.mainThread()) // listen calls on main thread
                    .subscribe(object -> { //receive events here
                        if (object instanceof Trigger.Increment) {
                            fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) + 1));
                        } else if (object instanceof Trigger.Decrement) {
                            if (Integer.parseInt(fabCounter.getText().toString()) != 0)
                                fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) - 1));
                        } else if (object instanceof Trigger.Reset) {
                            fabCounter.setText("0");
                        }
                    });

发送/触发事件
 MyApplication mApp = (App) getApplicationContext();
 //increment
 mApp
    .reactiveTrigger()
    .send(new Trigger.Increment());

 //decrement
 mApp
    .reactiveTrigger()
    .send(new Trigger.Decrement());

以上库的完整实现示例 -> RxTrigger


subscribeOn(Schedulers.io()) 的目的是什么? - Piotr Aleksander Chmielowski
除非您想在后台线程中执行某些操作,否则可以选择不使用 subscribeOn、observeOn。 - Aks4125

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接