はじめに
前回まででAPIからデータを取得してViewModelまで持ってこれました。
今回でViewModelからViewに持ってくる部分、イベント処理について解説します。
前提条件
こちらで説明するコードの前提条件はこちらを参照願います。
イベント処理の必要性について
ViewModelからView(ActivityとFragment)に対して非同期処理(apiなど)の結果を通知する必要があります。
このとき、同期処理で戻り値を受け取るとMainThreadが固まってしまいます。
またViewModelがViewの参照を持つとViewModelでライフサイクルを意識する必要がありますし、
ViewModelの単体テスト時にViewが必要となってしまいイマイチです。
そのため、イベントを用いてViewが生きているときのみ受け取るようにしています。
RxBusの選定理由。
イベント処理用のライブラリとしては有名なものにEventBusがあります。
今回RxBusを利用した理由としては、
EventBusの通知がわかりづらいという話があったのでRxBusにしました。
これはEventBusがグローバルにイベントを登録するのが原因ということですが、
いま調べると以下の様に実装によるみたいですね。
https://qiita.com/subaru44k/items/27cd4828edc65cd9d7a6
そういう意味では学習コストも考えてEventBusでも良かったかもしれませんが、
RxBusを使うことで使用するライブラリを非同期処理と統合できるというメリットもあります。
なお、RxJavaを使用しているからといってRxBusを使用しなければならない理由はないので、
RxJavaとEventBusでもOKです。
このあたりはチームの状況にあった選択にすれば良いと思います。
RxBusによるイベントの仕組み
RxBusでは、使用する箇所(例えばViewとViewModelとか)で同じRxBusのインスタンスにアクセスすることで、
イベントのやりとりを可能にしています。
RxBusのインスタンスはシングルトンで取得するようにしています。
これでイベントの登録と監視がどこからでも疎結合にできるようになっています。
なお、当初はApplicationにインスタンスを持たせていましたが、
単体テストを実行する際にApplicationがなくて実行できないのでシングルトンにしました。
AndroidTestでテスト実行すれば動くのでそれでも良かったかなぁと思いつつ、
いまはシングルトンにしています。
RxBusとライフサイクル
Viewで利用する場合、登録したイベントをライフサイクルに沿って解放してあげないと、
リークしたり、参照がなくてエラーになってしまう可能性があります。
詳細はあとで説明しますが、ActivityやFragmentが裏に回ったり解放される際に、
イベントの監視も解除する必要があります。
サンプルコード
RxBusの実装。
まずはRxBusを定義します。
ソースはこちら。
public class RxBus {
private final PublishSubject<Object> bus = PublishSubject.create();
private static RxBus rxBus;
private RxBus() {
}
public static RxBus getInstance() {
if (rxBus == null) {
rxBus = new RxBus();
}
return rxBus;
}
public void send(final Object event) {
bus.onNext(event);
}
public Observable<Object> toObservable() {
return bus;
}
public boolean hasObservers() {
return bus.hasObservers();
}
}
実際にイベントを登録する PublishSubject
のインスタンスを定義し、
シングルトンとして自インスタンスを返す定義しておきます。
RxBusでイベント送信する例
実際にRxBusを利用して、APIから非同期で取得した結果をViewに渡す処理です。
ソースはこちら。
GithubRepositoryApiCompleteEventEntity eventResult = new GithubRepositoryApiCompleteEventEntity();
eventResult.setListItems(data);
if (response.getItems() != null && response.getItems().size() > 0) {
eventResult.setResult(true);
}
// イベントを呼び出す
RxBus.getInstance().send(eventResult);
GithubRepositoryApiCompleteEventEntity
というイベントのやりとりに用いるEntityに取得したデータと、取得結果を格納します。
このインスタンスをイベントの引数として指定、イベント送信します。
RxBusでイベント受信する例
RxBusで送信されたイベントを受信する処理です。
ソースはこちら。
this.compositeDisposable = new CompositeDisposable();
this.compositeDisposable.add(RxBus.getInstance()
.toObservable()
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Object>() {
@Override
public void accept(Object obj) {
if (obj instanceof GithubRepositoryApiCompleteEventEntity) {
// 結果をリスト表示
if (((GithubRepositoryApiCompleteEventEntity) obj).isResult()) {
RecyclerView list = view.findViewById(R.id.githubRepositoryList);
list.setLayoutManager(new LinearLayoutManager(view.getContext()));
list.setAdapter(new GithubRepositoryListAdapter(((GithubRepositoryApiCompleteEventEntity) obj).getListItems()));
list.setVisibility(View.VISIBLE);
list.addItemDecoration(new DividerItemDecoration(view.getContext(), DividerItemDecoration.VERTICAL));
}
view.findViewById(R.id.progressBar).setVisibility(View.GONE);
}
// それ以外のイベントは処理しない
}
}));
ExpApplication.getInstance().getViewModelLocator().getGithubRepositoryListViewModel().getRepositoryListData();
// 〜中略〜
@Override
public void onStop() {
super.onStop();
this.compositeDisposable.clear();
}
@Override
public void onDestroy() {
super.onDestroy();
this.compositeDisposable.clear();
}
CompositeDisposable
のインスタンスを定義して、
これにRxBusイベント受信処理をセットします。
onStop()やonDestroy()でCompositeDisposable.clear()
を呼び監視解除しておくことで、
ライフサイクルに沿って処理させることができます。
RxBusイベント受信処理は.toObservable()
を呼んだあと、
.observeOn(AndroidSchedulers.mainThread())
で処理するスレッドを指定します。
画面をいじるので今回はMainThreadにしていますが、
Schedulers.newThread()
でMainとは違うスレッドで動かすこともできます。
.subscribe(new Consumer<Object>() {
以降で具体的な受信処理を記載します。
public void accept(Object obj) {
以下が受信処理なのですが、
送信処理で指定した引数がObject型の変数として送られてきます。
if (obj instanceof GithubRepositoryApiCompleteEventEntity) {
で型を判定して、
受信したいイベントなのかどうか判定しています。
受信対象のイベントであれば処理します。
おわりに
というわけで、RxBusの説明をしてみました。
次回はroomによるsqLiteを説明したいと思います。