Upload
fumihiko-shiroyama
View
1.832
Download
0
Embed Size (px)
Citation preview
Subject 入門Rx Ja Night 2016 #1
2016-02-25
• 白山 文彦
• 株式会社マナボ 技術者
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); } ... }
/** * 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); } ... }
Observer でもあり Observable でもある
ObserverとしてObservableをsubscribeできる
Observableとして他のObserverからsubscribeされる
from wikimedia commons
Source Observable
Subject
Observer
最も基本的な例
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); }
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
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
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
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の外から 来ていることに注目!
HOT Observableと呼ばれたりする
Subject の種類
• PublishSubject
• BehaviorSubject
• AsyncSubject
• ReplaySubject
PublishSubject
• 最も基本的なSubject
• subscribeしたObserverに後続のイベントをそのままバイパスする
ここでsubscribeしたObserverは 3つとも受け取る
ここでsubscribeしたObserverはこの1つだけ
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
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
BehaviorSubject
• 直前の値(ない場合は初期値)をキャッシュし、Observerに即座に返すSubject
• 後続のイベントはそのままバイパスする
Default Value
Most Recent Value
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));
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...
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
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
AsyncSubject
• Subject#onCompletedが呼ばれたらその直前のonNext(T)をObserverに1回だけ渡す
• Subject#onNextが何度呼ばれてもonCompletedまではObserverには何も渡らない
• REST通信など、onNext, onCompletedが1セットみたいなケースで便利そう
Last Value
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));
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
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
ReplaySubject
• subscribeしたObservableから送出された値をすべてキャッシュし、Observerがsubscribeしてきたらそれをすべて渡すようなSubject
• 後続のイベントもそのままバイパスする
Cached Value
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));
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
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
Subject の応用例
EventBus
WTF...
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); } }
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
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
public class BusProvider { private static final RxEventBus eventBus = new RxEventBus(); private BusProvider() { } public static RxEventBus getInstance() { return eventBus; } }
Singleton
public class ItemSelectEvent { private int position; public ItemSelectEvent(int position) { this.position = position; } public int getPosition() { return position; } }
Simple Event
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(); } }
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とほぼ同じ!
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(); } }
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(); } }
ここもそっくり!
https://github.com/srym/RxEventBus
その他TIPS、質疑応答