66
Subject 入門 Rx Ja Night 2016 #1 2016-02-25

RxJava - Subject 入門

Embed Size (px)

Citation preview

Page 1: RxJava - Subject 入門

Subject 入門Rx Ja Night 2016 #1

2016-02-25

Page 2: RxJava - Subject 入門

• 白山 文彦

• 株式会社マナボ 技術者

Page 3: RxJava - Subject 入門

Subject とはなんだろうか?

Page 4: RxJava - Subject 入門

/** * Represents an object that is both an Observable and an Observer. */public abstract class Subject<T, R> extends Observable<R> implements Observer<T> { protected Subject(OnSubscribe<R> onSubscribe) { super(onSubscribe); } ... }

Page 5: RxJava - Subject 入門

/** * Represents an object that is both an Observable and an Observer. */public abstract class Subject<T, R> extends Observable<R> implements Observer<T> { protected Subject(OnSubscribe<R> onSubscribe) { super(onSubscribe); } ... }

Page 6: RxJava - Subject 入門

Observer でもあり Observable でもある

Page 7: RxJava - Subject 入門

ObserverとしてObservableをsubscribeできる

Page 8: RxJava - Subject 入門

Observableとして他のObserverからsubscribeされる

Page 9: RxJava - Subject 入門

from wikimedia commons

Page 10: RxJava - Subject 入門
Page 11: RxJava - Subject 入門

Source Observable

Page 12: RxJava - Subject 入門

Subject

Page 13: RxJava - Subject 入門

Observer

Page 14: RxJava - Subject 入門

最も基本的な例

Page 15: RxJava - Subject 入門

PublishSubject<String> subject = PublishSubject.create(); subject.subscribe( val -> Log.i(TAG, val), error -> Log.e(TAG, error.getMessage(), error)); try { subject.onNext("FOO"); subject.onNext("BAR"); subject.onNext("BAZ"); subject.onCompleted(); } catch (Exception e) { subject.onError(e); }

Page 16: RxJava - Subject 入門

PublishSubject<String> subject = PublishSubject.create(); subject.subscribe( val -> Log.i(TAG, val), error -> Log.e(TAG, error.getMessage(), error)); try { subject.onNext("FOO"); subject.onNext("BAR"); subject.onNext("BAZ"); subject.onCompleted(); } catch (Exception e) { subject.onError(e); }

create

Page 17: RxJava - Subject 入門

PublishSubject<String> subject = PublishSubject.create(); subject.subscribe( val -> Log.i(TAG, val), error -> Log.e(TAG, error.getMessage(), error)); try { subject.onNext("FOO"); subject.onNext("BAR"); subject.onNext("BAZ"); subject.onCompleted(); } catch (Exception e) { subject.onError(e); }

subscribe

Page 18: RxJava - Subject 入門

PublishSubject<String> subject = PublishSubject.create(); subject.subscribe( val -> Log.i(TAG, val), error -> Log.e(TAG, error.getMessage(), error)); try { subject.onNext("FOO"); subject.onNext("BAR"); subject.onNext("BAZ"); subject.onCompleted(); } catch (Exception e) { subject.onError(e); }

bypass

Page 19: RxJava - Subject 入門

PublishSubject<String> subject = PublishSubject.create(); subject.subscribe( val -> Log.i(TAG, val), error -> Log.e(TAG, error.getMessage(), error)); try { subject.onNext("FOO"); subject.onNext("BAR"); subject.onNext("BAZ"); subject.onCompleted(); } catch (Exception e) { subject.onError(e); }

bypass

値がObservableの外から 来ていることに注目!

Page 20: RxJava - Subject 入門

HOT Observableと呼ばれたりする

Page 21: RxJava - Subject 入門

Subject の種類

Page 22: RxJava - Subject 入門

• PublishSubject

• BehaviorSubject

• AsyncSubject

• ReplaySubject

Page 23: RxJava - Subject 入門

PublishSubject

Page 24: RxJava - Subject 入門

• 最も基本的なSubject

• subscribeしたObserverに後続のイベントをそのままバイパスする

Page 25: RxJava - Subject 入門
Page 26: RxJava - Subject 入門

ここでsubscribeしたObserverは 3つとも受け取る

Page 27: RxJava - Subject 入門

ここでsubscribeしたObserverはこの1つだけ

Page 28: RxJava - Subject 入門

PublishSubject<String> subject = PublishSubject.create(); subject.subscribe( val -> Log.i(TAG, val), error -> Log.e(TAG, error.getMessage(), error)); try { subject.onNext("FOO"); subject.onNext("BAR"); subject.onNext("BAZ"); subject.onCompleted(); } catch (Exception e) { subject.onError(e); }

observer

Page 29: RxJava - Subject 入門

PublishSubject<String> subject = PublishSubject.create(); subject.subscribe( val -> Log.i(TAG, val), error -> Log.e(TAG, error.getMessage(), error)); try { subject.onNext("FOO"); subject.onNext("BAR"); subject.onNext("BAZ"); subject.onCompleted(); } catch (Exception e) { subject.onError(e); }

bypass

Page 30: RxJava - Subject 入門

BehaviorSubject

Page 31: RxJava - Subject 入門

• 直前の値(ない場合は初期値)をキャッシュし、Observerに即座に返すSubject

• 後続のイベントはそのままバイパスする

Page 32: RxJava - Subject 入門
Page 33: RxJava - Subject 入門

Default Value

Page 34: RxJava - Subject 入門

Most Recent Value

Page 35: RxJava - Subject 入門

Observable<String> observable = Observable.just("FOO", "BAR", "BAZ"); BehaviorSubject<String> behaviorSubject = BehaviorSubject.create("init val");

observable.subscribe( behaviorSubject::onNext);

behaviorSubject.subscribe( val -> Log.i(TAG, val), error -> Log.e(TAG, error.getMessage(), error));

Page 36: RxJava - Subject 入門

Observable<String> observable = Observable.just("FOO", "BAR", "BAZ"); BehaviorSubject<String> behaviorSubject = BehaviorSubject.create("init val");

observable.subscribe( behaviorSubject::onNext);

behaviorSubject.subscribe( val -> Log.i(TAG, val), error -> Log.e(TAG, error.getMessage(), error));

FOO, BAR, BAZ...

Page 37: RxJava - Subject 入門

Observable<String> observable = Observable.just("FOO", "BAR", "BAZ"); BehaviorSubject<String> behaviorSubject = BehaviorSubject.create("init val");

observable.subscribe( behaviorSubject::onNext);

behaviorSubject.subscribe( val -> Log.i(TAG, val), error -> Log.e(TAG, error.getMessage(), error));

BAZ

Page 38: RxJava - Subject 入門

Observable<String> observable = Observable.just("FOO", "BAR", "BAZ"); BehaviorSubject<String> behaviorSubject = BehaviorSubject.create("init val");

observable.subscribe( behaviorSubject::onNext);

behaviorSubject.subscribe( val -> Log.i(TAG, val), error -> Log.e(TAG, error.getMessage(), error));

BAZ

Page 39: RxJava - Subject 入門

AsyncSubject

Page 40: RxJava - Subject 入門

• Subject#onCompletedが呼ばれたらその直前のonNext(T)をObserverに1回だけ渡す

• Subject#onNextが何度呼ばれてもonCompletedまではObserverには何も渡らない

• REST通信など、onNext, onCompletedが1セットみたいなケースで便利そう

Page 41: RxJava - Subject 入門
Page 42: RxJava - Subject 入門

Last Value

Page 43: RxJava - Subject 入門

Observable<String> sourceObservable = Observable.just("ONE"); AsyncSubject<String> asyncSubject = AsyncSubject.create(); sourceObservable.subscribe( data -> { asyncSubject.onNext(data); asyncSubject.onCompleted(); }, error -> Log.e(TAG, error.getMessage(), error)); asyncSubject.subscribe( data -> Log.i(TAG, data), error -> Log.e(TAG, error.getMessage(), error));

Page 44: RxJava - Subject 入門

Observable<String> sourceObservable = Observable.just("ONE"); AsyncSubject<String> asyncSubject = AsyncSubject.create(); sourceObservable.subscribe( data -> { asyncSubject.onNext(data); asyncSubject.onCompleted(); }, error -> Log.e(TAG, error.getMessage(), error)); asyncSubject.subscribe( data -> Log.i(TAG, data), error -> Log.e(TAG, error.getMessage(), error));

ONE

Page 45: RxJava - Subject 入門

Observable<String> sourceObservable = Observable.just("ONE"); AsyncSubject<String> asyncSubject = AsyncSubject.create(); sourceObservable.subscribe( data -> { asyncSubject.onNext(data); asyncSubject.onCompleted(); }, error -> Log.e(TAG, error.getMessage(), error)); asyncSubject.subscribe( data -> Log.i(TAG, data), error -> Log.e(TAG, error.getMessage(), error));

ONE

Page 46: RxJava - Subject 入門

ReplaySubject

Page 47: RxJava - Subject 入門

• subscribeしたObservableから送出された値をすべてキャッシュし、Observerがsubscribeしてきたらそれをすべて渡すようなSubject

• 後続のイベントもそのままバイパスする

Page 48: RxJava - Subject 入門
Page 49: RxJava - Subject 入門

Cached Value

Page 50: RxJava - Subject 入門

Observable<String> sourceObservable = Observable.just("ONE", "TWO", "THREE", "FOUR", "FIVE"); ReplaySubject<String> replaySubject = ReplaySubject.create(); sourceObservable.subscribe( replaySubject::onNext); replaySubject.subscribe( data -> Log.i(TAG, data), error -> Log.e(TAG, error.getMessage(), error));

Page 51: RxJava - Subject 入門

Observable<String> sourceObservable = Observable.just("ONE", "TWO", "THREE", "FOUR", "FIVE"); ReplaySubject<String> replaySubject = ReplaySubject.create(); sourceObservable.subscribe( replaySubject::onNext); replaySubject.subscribe( data -> Log.i(TAG, data), error -> Log.e(TAG, error.getMessage(), error));

cache

Page 52: RxJava - Subject 入門

Observable<String> sourceObservable = Observable.just("ONE", "TWO", "THREE", "FOUR", "FIVE"); ReplaySubject<String> replaySubject = ReplaySubject.create(); sourceObservable.subscribe( replaySubject::onNext); replaySubject.subscribe( data -> Log.i(TAG, data), error -> Log.e(TAG, error.getMessage(), error));

ONE, TWO, THREE, FOUR, FIVE

Page 53: RxJava - Subject 入門

Subject の応用例

Page 54: RxJava - Subject 入門

EventBus

Page 55: RxJava - Subject 入門

WTF...

Page 56: RxJava - Subject 入門

public class RxEventBus { private final Subject<Object, Object> bus = new SerializedSubject<>(PublishSubject.create()); public void post(Object event) { bus.onNext(event); } public <T> Subscription subscribe(Class<T> clazz, Action1<T> onNext) { return bus.ofType(clazz).subscribe(onNext); } }

Page 57: RxJava - Subject 入門

public class RxEventBus { private final Subject<Object, Object> bus = new SerializedSubject<>(PublishSubject.create()); public void post(Object event) { bus.onNext(event); } public <T> Subscription subscribe(Class<T> clazz, Action1<T> onNext) { return bus.ofType(clazz).subscribe(onNext); } }

Thread Safe Subject

Page 58: RxJava - Subject 入門

public class RxEventBus { private final Subject<Object, Object> bus = new SerializedSubject<>(PublishSubject.create()); public void post(Object event) { bus.onNext(event); } public <T> Subscription subscribe(Class<T> clazz, Action1<T> onNext) { return bus.ofType(clazz).subscribe(onNext); } } bypass

Page 59: RxJava - Subject 入門

public class BusProvider { private static final RxEventBus eventBus = new RxEventBus(); private BusProvider() { } public static RxEventBus getInstance() { return eventBus; } }

Singleton

Page 60: RxJava - Subject 入門

public class ItemSelectEvent { private int position; public ItemSelectEvent(int position) { this.position = position; } public int getPosition() { return position; } }

Simple Event

Page 61: RxJava - Subject 入門

public class Adapter extends RecyclerView.Adapter<ViewHolder> { private LayoutInflater layoutInflater; private List<String> texts; public Adapter(Context context) { layoutInflater = LayoutInflater.from(context); texts = new ArrayList<>(); texts.add("あああああああああああ"); ... } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = layoutInflater.inflate(R.layout.list_item, parent, false); ViewHolder viewHolder = new ViewHolder(v); viewHolder.itemView.setOnClickListener(view -> { int position = viewHolder.getAdapterPosition(); if (position != RecyclerView.NO_POSITION) { BusProvider.getInstance().post(new ItemSelectEvent(position)); } }); return viewHolder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.bind(texts.get(position)); } @Override public int getItemCount() { return texts.size(); } }

Page 62: RxJava - Subject 入門

public class Adapter extends RecyclerView.Adapter<ViewHolder> { private LayoutInflater layoutInflater; private List<String> texts; public Adapter(Context context) { layoutInflater = LayoutInflater.from(context); texts = new ArrayList<>(); texts.add("あああああああああああ"); ... } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = layoutInflater.inflate(R.layout.list_item, parent, false); ViewHolder viewHolder = new ViewHolder(v); viewHolder.itemView.setOnClickListener(view -> { int position = viewHolder.getAdapterPosition(); if (position != RecyclerView.NO_POSITION) { BusProvider.getInstance().post(new ItemSelectEvent(position)); } }); return viewHolder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { holder.bind(texts.get(position)); } @Override public int getItemCount() { return texts.size(); } }

ottoとほぼ同じ!

Page 63: RxJava - Subject 入門

public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); @Bind(R.id.recycler_view) RecyclerView recyclerView; private Subscription subscription; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setHasFixedSize(true); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(new Adapter(this)); } @Override protected void onResume() { super.onResume(); subscription = BusProvider.getInstance().subscribe( ItemSelectEvent.class, e -> Toast.makeText(this, "position: " + e.getPosition(), Toast.LENGTH_SHORT).show() ); } @Override protected void onPause() { subscription.unsubscribe(); super.onPause(); } }

Page 64: RxJava - Subject 入門

public class MainActivity extends AppCompatActivity { private static final String TAG = MainActivity.class.getSimpleName(); @Bind(R.id.recycler_view) RecyclerView recyclerView; private Subscription subscription; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setHasFixedSize(true); recyclerView.setItemAnimator(new DefaultItemAnimator()); recyclerView.setAdapter(new Adapter(this)); } @Override protected void onResume() { super.onResume(); subscription = BusProvider.getInstance().subscribe( ItemSelectEvent.class, e -> Toast.makeText(this, "position: " + e.getPosition(), Toast.LENGTH_SHORT).show() ); } @Override protected void onPause() { subscription.unsubscribe(); super.onPause(); } }

ここもそっくり!

Page 65: RxJava - Subject 入門

https://github.com/srym/RxEventBus

Page 66: RxJava - Subject 入門

その他TIPS、質疑応答