【potatotips #30】rxjavaを活用する3つのユースケース

27
Androidアプリ開発で RxJavaを活用する 3つのユースケース Hiroyuki Kusu ( @hkusu_ ) 2016/06/23 potatotips #30

Upload: hiroyuki-kusu

Post on 23-Jan-2018

2.056 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: 【Potatotips #30】RxJavaを活用する3つのユースケース

Androidアプリ開発でRxJavaを活用する3つのユースケース

Hiroyuki Kusu ( @hkusu_ )

2016/06/23 potatotips #30

Page 2: 【Potatotips #30】RxJavaを活用する3つのユースケース
Page 3: 【Potatotips #30】RxJavaを活用する3つのユースケース

①非同期処理の結果うけとり

②イベントの送信/購読

③データの変更監視

※今回はデータ加工、FRP的な話はしません

※本スライド上のサンプルコードは Java 8 およびRetrolambdaの利用を前提としています

Page 4: 【Potatotips #30】RxJavaを活用する3つのユースケース

①非同期処理の結果うけとり

②イベントの送信/購読

③データの変更監視

Page 5: 【Potatotips #30】RxJavaを活用する3つのユースケース

doSomethingAsnc()

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(aStringList -> {

// … 結果を受け取って何か

});

コールバックを廃して同期的に書ける

Observable<T>を返す非同期な処理

Page 6: 【Potatotips #30】RxJavaを活用する3つのユースケース

Observable.create(subscriber -> {

// ... 裏スレッドで何か

subscriber.onNext("something");

subscriber.onCompleted();

})

.subscribeOn(Schedulers.io()) // 裏スレッドを指定

.observeOn(AndroidSchedulers.mainThread()) // メインスレッドを指定

.subscribe(res -> {

// ... メインスレッドで結果を受け取って何か

});

ちなみに重たい処理を裏スレッドで実行させたい時にも使える(AsyncTask、AsyncTaskLoaderの替り)

Page 7: 【Potatotips #30】RxJavaを活用する3つのユースケース

①非同期処理の結果うけとり

②イベントの送信/購読

③データの変更監視

Page 8: 【Potatotips #30】RxJavaを活用する3つのユースケース

購読側

イベント仲介用のクラス(シングルトン)

Subject

送信側

早い話が Otto や EventBusのようなもの

コールバックを引き回すのが煩雑な場合に(例:Activity 間の通知、ListViewの操作イベント取得など)

イベントの送信

イベントの購読

Page 9: 【Potatotips #30】RxJavaを活用する3つのユースケース

public class RxEventBus {

private final Subject<Object, Object> subject

= new SerializedSubject<>(PublishSubject.create());

public <T> Subscription onEvent(Class<T> clazz, Action1<T> handler) {

return subject

.ofType(clazz)

.subscribe(handler);

}

public void post(Object event) {

subject.onNext(event);

}

}

RxEventBus.java(前項におけるイベント仲介用のクラス)

内部にSubjectを保持

継続的にイベントを発生させるにはSubject を用いる

Page 10: 【Potatotips #30】RxJavaを活用する3つのユースケース

public class RxEventBus {

private final Subject<Object, Object> subject

= new SerializedSubject<>(PublishSubject.create());

public <T> Subscription onEvent(Class<T> clazz, Action1<T> handler) {

return subject

.ofType(clazz)

.subscribe(handler);

}

public void post(Object event) {

subject.onNext(event);

}

}

RxEventBus.java(前項におけるイベント仲介用のクラス)

イベントの購読用メソッド

イベントクラス名でフィルタ

Page 11: 【Potatotips #30】RxJavaを活用する3つのユースケース

public class RxEventBus {

private final Subject<Object, Object> subject

= new SerializedSubject<>(PublishSubject.create());

public <T> Subscription onEvent(Class<T> clazz, Action1<T> handler) {

return subject

.ofType(clazz)

.subscribe(handler);

}

public void post(Object event) {

subject.onNext(event);

}

}

RxEventBus.java(前項におけるイベント仲介用のクラス)

イベントの送信用メソッド

Page 12: 【Potatotips #30】RxJavaを活用する3つのユースケース

rxEventBus.post(new SomeEvent());

rxEventBus.onEvent(SomeEvent.class, event -> {

// … 結果を受け取って何か

});

イベントの送信

イベントの購読

イベント仲介用のクラスのインスタンスを共有(シングルトン等)しておけば、アプリケーションのどこからでもイベントを送れる

Page 13: 【Potatotips #30】RxJavaを活用する3つのユースケース

①非同期処理の結果うけとり

②イベントの送信/購読

③データの変更監視

Page 14: 【Potatotips #30】RxJavaを活用する3つのユースケース

データを保持するクラス

Subject購読側

購読

データ変更

Page 15: 【Potatotips #30】RxJavaを活用する3つのユースケース

public class SomeRepository {

private final Subject<String, String> subject

= new SerializedSubject<>(PublishSubject.create());

public final Observable<String> getObservable() {

return subject;

}

private void notify(String string) {

subject.onNext(string);

}

// ... 以降、CRUDのコード

}

内部にSubjectを保持

SomeRepository.java(前項におけるデータを保持するクラス)

Page 16: 【Potatotips #30】RxJavaを活用する3つのユースケース

public class SomeRepository {

private final Subject<String, String> subject

= new SerializedSubject<>(PublishSubject.create());

public final Observable<String> getObservable() {

return subject;

}

private void notify(String string) {

subject.onNext(string);

}

// ... 以降、CRUDのコード

}

SomeRepository.java(前項におけるデータを保持するクラス)

SubjectをObservable<T>として外部へ公開

Page 17: 【Potatotips #30】RxJavaを活用する3つのユースケース

public class SomeRepository {

private final Subject<String, String> subject

= new SerializedSubject<>(PublishSubject.create());

public final Observable<String> getObservable() {

return subject;

}

private void notify(String string) {

subject.onNext(string);

}

// ... 以降、CRUDのコード

}

SomeRepository.java(前項におけるデータを保持するクラス)

データ変更を通知するprivate メソッド

※データのCRUD時に notify() をコールする規則とする

Page 18: 【Potatotips #30】RxJavaを活用する3つのユースケース

someRepository.getObservable()

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(aString -> {

// ... 結果を受け取って何か

});

購読側

データが変更される度にストリームが流れてくる

Page 19: 【Potatotips #30】RxJavaを活用する3つのユースケース

データを保持するクラス

Activity/

Fragment 購読

ビューへの反映

何かしらのCRUD操作

データ

『データに対する』ビューの描画処理が宣言的に定義できる。データの流れが一方向なので分かりやすい

関心の分離

Page 20: 【Potatotips #30】RxJavaを活用する3つのユースケース

TIPS

Page 21: 【Potatotips #30】RxJavaを活用する3つのユースケース

・購読の一括解除ができる

・Activity の onPause() 等で購読を停止したい場合、一度 unscribe() すると add()できなくなる問題

⇒ unsubscribe() でなく clear() を使う

CompositeSubscription

Page 22: 【Potatotips #30】RxJavaを活用する3つのユースケース

doSomethingAsnc()

.subscribeOn(Schedulers.io())

.observeOn(AndroidSchedulers.mainThread())

.subscribe(aStringList -> {

// ... 結果を受け取って何か

});

型名を示す変数名をつけると分かりやすいかも?

Retrolambdaを利用していてストリーム中のデータの型がよく分からなくなる問題

例)List<String> の場合

Page 23: 【Potatotips #30】RxJavaを活用する3つのユースケース

public class Tuple3<T1, T2, T3> {

private final T1 t1;

private final T2 t2;

private final T3 t3;

private Tuple3(T1 t1, T2 t2, T3 t3) {

this.t1 = t1;

this.t2 = t2;

this.t3 = t3;

}

public T1 get1() {

return t1;

}

public T2 get2() {

return t2;

}

public T3 get3() {

return t3;

}

public static <T1, T2, T3> Tuple3<T1, T2, T3> create(T1 t1, T2 t2, T3 t3) {

return new Tuple3<>(t1, t2, t3);

}

}

3つ以上の時は Tuple 的なクラスを用意する

Observable<T> で複数の値を扱いたい場合

Observable<Pair<String, List<Integer>>>

2つの場合は Pair が使える

Page 24: 【Potatotips #30】RxJavaを活用する3つのユースケース

Sample code

hkusu/android-rxjava-app-sample

Page 25: 【Potatotips #30】RxJavaを活用する3つのユースケース

THANKS!

Page 26: 【Potatotips #30】RxJavaを活用する3つのユースケース

予備スライド

Page 27: 【Potatotips #30】RxJavaを活用する3つのユースケース

Activ

ity / F

rag

me

nt

Use

Ca

se層

Reposito

ry層

Se

rvic

e層

(Andro

id の

Serv

iceではない

)

DI DI DI

Ob

servable<T>

Ob

servable

<T>

Ob

servable

<T>

裏スレッド( Schedulers.io() )で実行

メインスレッド( AndroidSchedulers

.mainThread() )で購読