Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Simplify Your Code with Rx

Simplify Your Code with Rx

Show how you can simplify your class design with Rx (reactive extensions), with implementation in RxJava.

ferry tanu

March 18, 2016
Tweet

Other Decks in Programming

Transcript

  1. ▪ What is VideoSDK? ▪ What is Rx? ▪ Show

    me the code ▪ Q&A Agenda 2
  2. What is VideoSDK? ▪ A video player wrapper ▪ Purpose:

    to make it easier for app to integrate with video players ▪ Manage between ads and content ▪ Components: › Remote API call (aka HTTP call) › Instrumentation › UI/UX according to company brand › Error handling
  3. What is VideoSDK? ▪ A video player wrapper ▪ Purpose:

    to make it easier for app to integrate with video players ▪ Manage between ads and content ▪ Components: › Remote API call (aka HTTP call) › Instrumentation › UI/UX according to company brand › Error handling
  4. Problem: • Player manages the clock lifecycle • Clock implements

    player listener → ugly! • Clock need to maintain state • Frozen detection (event) depends on player state • Update the seekbar UI to reflect playback position • Detect Inactivity
  5. The birth of the god object god object media player

    playback listener timer instrume ntation clock api service ads ui
  6. The Observer pattern done right (reactivex.io) ReactiveX is a combination

    of the best ideas from the Observer pattern, the Iterator pattern, and functional programming Reactive Extensions (Rx)
  7. Imperative Integer[] list= new Integer[]{1, 2, 3, 4, 5}; int

    acc = 0; for ( int i = 0; i < list.length; i++ ) { acc = acc + 3 * list[i]; } Functional Programming Build Complexity out of Simplicity using Composition RxJava sumInteger( from(list) .map((num) -> { 3 * num }) ).subscribe(/* do with the total */); Haskell sum (map (3*) list)
  8. Create Observable MediaPlayer mObservableMediaPlayer = Observable.create(new PlayerEventOnSubscribe(mediaPlayer)).publish(); class PlayerEventOnSubscribe implements

    Observable.OnSubscribe<Integer> { @Override public void call(final Subscriber<? super Integer> subscriber) { PlayerEventListener listener = new PlayerEventListener() { @Override public void onPlaying() { onNext(subscriber, PLAYING); } @Override public void onPaused() { onNext(subscriber,PAUSED); } @Override public void onReleased() { if (!subscriber.isUnsubscribed()) { onNext(subscriber, RELEASED); subscriber.unsubscribe(); } } }; player.setPlayerEventListener(listener); subscriber.add(new MainThreadSubscription() { @Override protected void onUnsubscribe() { player.setPlayerEventListener(null); } }); } void onNext(Subscriber<? super Integer> subscriber, int state) { if (!subscriber.isUnsubscribed()) { subscriber.onNext(state); } } Unsubscribe
  9. Observable Media Player interface PlayerEventListener { void onPrepared(); void onPlaying();

    void onPaused(); .... } Observable<Integer> prepared() { return mMediaPlayerObservable.filter(equalTo(PREPARED)); } Observable<Integer> playing() { return mMediaPlayerObservable.filter(equalTo(PLAYING)); } Observable<Integer> paused() { return mMediaPlayerObservable.filter(equalTo(PAUSED)); }
  10. Create Observable Playback Clock mClockObservable = Observable.interval(1, SECONDS) .map((aLong) ->

    mediaPlayer.getCurrentPosition()) .publish(); map time to playback position Timer with interval 1 second
  11. Manage Clock Lifecycle // clock start when player start to

    play playing() .subscribe((i) -> { mSubscription = mClockObservable.connect(); }, RxLog.e(TAG, "ouch!!")); // stop when player is pause or complete or released Observable.merge(paused(), playComplete(), released()) .subscribe((integer) -> { mSubscription.unsubscribe(); }, RxLog.e(TAG, "ouch!!")); player clock subscribe playing paused / complete/ released
  12. Topic are composable changed frozen map time to playback position

    distinct Calculate frozen time Filter >= gracePeriod Timer with interval 1 second
  13. Observable Playback Clock mClockObservable = Observable.interval(1, SECONDS) .map((aLong) -> mediaPlayer.getCurrentPosition())

    .publish(); Observable<Long> changed() { return mClockObservable .distinctUntilChanged(); } Observable<Integer> frozen(final int gracePeriod) { return mClockObservable .map(calculateFrozenTime()) .filter((frozenTime) -> frozenTime >= gracePeriod); } Map the clock value to playback position Only emit when position changed changed() .subscribe(()-> {/*update seekbar position*/}); frozen() .subscribe(()-> {/*reload player*/}); Emit when frozen for more than grace period
  14. Detect Inactivity Observable<Integer> frozen(final int gracePeriod) { return mClockObservable .map(calculateFrozenTime())

    .filter((frozenTime) -> frozenTime >= gracePeriod }); Func1<Long, Integer> calculateFrozenTime() { return new Func1<Long, Integer>() { Long lastPlayTime = 0L; Integer frozenTime = 0; @Override public Integer call(Long currentPlayTime) { if (!lastPlayTime.equals(currentPlayTime)) { lastPlayTime = currentPlayTime; frozenTime = 0; } else { frozenTime++; } return frozenTime; } }; }
  15. Either start to play or released void shouldStartPlayIn(long timeoutS, Action1<Throwable>

    actionWhenFailed) { Observable.merge(prepared(), released()) .first() prepared release first
  16. Either start to play or released in X seconds void

    shouldStartPlayIn(long timeoutS, Action1<Throwable> actionWhenFailed) { Observable.merge(prepared(), released()) .first() .timeout(timeoutMs, SECONDS) prepared release first timeout
  17. Either start to play or released in X seconds ->

    action void shouldStartPlayIn(long timeoutS, Action1<Throwable> actionWhenFailed) { Observable.merge(prepared(), released()) .first() .timeout(timeoutMs, SECONDS) .doOnError(RxLog.e(TAG, "Video not playing in " + timeoutS)) .observeOn(AndroidSchedulers.mainThread()) .subscribe((i) -> {/* no action if the condition satisfied*/}, actionWhenFailed); } prepared release first timeout Do something Do nothing