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

Reactive Programming

Adrien Couque
December 22, 2015

Reactive Programming

Internal talk at Applidium (22 Dec 2015)

Adrien Couque

December 22, 2015
Tweet

More Decks by Adrien Couque

Other Decks in Technology

Transcript

  1. Problems to solve • Callback hell • Chaining operations •

    Threading is hard CompletionService Delayed Executor ExecutorService Future RunnableFuture ScheduledExecutorService ScheduledFuture ThreadFactory BrokenBarrierException CountDownLatch CyclicBarrier Exchanger ForkJoinPool ForkJoinWorkerThread Phaser RecursiveAction RecursiveTask ScheduledThreadPoolExecutor Semaphore Thread AsyncTask Loader IntentService
  2. Example: MyMTV Login Account User User User User User Profile

    Profile Profile Profile Profile Success!
  3. Example: RMN Slideshow: - collection of medias (images and videos)

    - images should be displayed as such - videos should display a preview
  4. Rx: Reactive Extensions • Functional Reactive Programming : 1997 (40

    years after Functional/OOP) • Started by Microsoft for .NET • RxJava: 2012 (Netflix) => ReactiveCocoa, RxAndroid
  5. Observable single items multiple items synchronous T getData() Iterable<T> getData()

    Collection<T> getData() asynchronous Future<T> getData() Observable<T> getData()
  6. Subscriber event Iterable (pull) Observable (push) retrieve data T next()

    onNext(T) discover error throws Exception onError(Exception) complete returns onCompleted()
  7. Creating an Observable Observable<String> o = Observable.from("a", "b", "c"); def

    list = [5, 6, 7, 8] Observable<Integer> o = Observable.from(list); Observable<String> o = Observable.just("one object");
  8. Creating an Observable Observable.just(t); Observable.just(t1, t2, t3, t4, t5, ...);

    Observable.from(iterable); Observable.range(start, end); Observable.empty(); Observable.error(throwable); Observable.never(); Observable.interval(interval, unit); Observable.timer(delay, period, unit);
  9. Creating an Observable func myJust<E>(element: E) -> Observable<E> { return

    create { observer in observer.on(.Next(element)) observer.on(.Completed) return NopDisposable.instance } } myJust(0) .subscribeNext { n in print(n) }
  10. map

  11. Solving RMN Observable<Media> medias = apiManager.getMediaSlideshow(exposition); Observable<URL> urls = medias.flatMap(media

    -> if (media.isImage()) { return Observable.just(media.getImageUrl()); } else { return apiManager.getYoutubePreviewImage(media); } );
  12. Solving RMN Observable<Media> medias = apiManager.getMediaSlideshow(exposition); Observable<URL> urls = medias.flatMap(media

    -> if (media.isImage()) { return Observable.just(media.getImageUrl()); } else { return apiManager.getYoutubePreviewImage(media); } );
  13. Solving RMN Observable<Media> medias = apiManager.getMediaSlideshow(exposition); Observable<URL> urls = medias.flatMap(media

    -> if (media.isImage()) { return Observable.just(media.getImageUrl()); } else { return apiManager.getYoutubePreviewImage(media); } );
  14. Solving RMN Observable<Media> medias = apiManager.getMediaSlideshow(exposition); Observable<URL> urls = medias.flatMap(media

    -> if (media.isImage()) { return Observable.just(media.getImageUrl()); } else { return apiManager.getYoutubePreviewImage(media); } );
  15. Solving RMN Observable<Media> medias = apiManager.getMediaSlideshow(exposition); Observable<URL> urls = medias.flatMap(media

    -> if (media.isImage()) { return Observable.just(media.getImageUrl()); } else { return apiManager.getYoutubePreviewImage(media); } );
  16. Solving MyMTV Login Account User User User User User Profile

    Profile Profile Profile Profile Success!
  17. Solving MyMTV Observable.just(mAccount).map(account -> return account.createUserIfNecessary(context); ).flatMap(account -> return Observable.from(account.getUserList().getUsers());

    ).filter(user -> return user.getProfiles().isEmpty(); ).flatMap(user -> return user.createNewProfile(); ).subscribe(onNext, onError, onCompleted);
  18. Solving MyMTV Observable.just(mAccount).map(account -> return account.createUserIfNecessary(context); ).flatMap(account -> return Observable.from(account.getUserList().getUsers());

    ).filter(user -> return user.getProfiles().isEmpty(); ).flatMap(user -> return user.createNewProfile(); ).subscribe(onNext, onError, onCompleted);
  19. Solving MyMTV Observable.just(mAccount).map(account -> return account.createUserIfNecessary(context); ).flatMap(account -> return Observable.from(account.getUserList().getUsers());

    ).filter(user -> return user.getProfiles().isEmpty(); ).flatMap(user -> return user.createNewProfile(); ).subscribe(onNext, onError, onCompleted);
  20. Solving MyMTV Observable.just(mAccount).map(account -> return account.createUserIfNecessary(context); ).flatMap(account -> return Observable.from(account.getUserList().getUsers());

    ).filter(user -> return user.getProfiles().isEmpty(); ).flatMap(user -> return user.createNewProfile(); ).subscribe(onNext, onError, onCompleted);
  21. Solving MyMTV Observable.just(mAccount).map(account -> return account.createUserIfNecessary(context); ).flatMap(account -> return Observable.from(account.getUserList().getUsers());

    ).filter(user -> return user.getProfiles().isEmpty(); ).flatMap(user -> return user.createNewProfile(); ).subscribe(onNext, onError, onCompleted);
  22. Solving MyMTV Observable.just(mAccount).map(account -> return account.createUserIfNecessary(context); ).flatMap(account -> return Observable.from(account.getUserList().getUsers());

    ).filter(user -> return user.getProfiles().isEmpty(); ).flatMap(user -> return user.createNewProfile(); ).subscribe(onNext, onError, onCompleted);
  23. Solving MyMTV Observable.just(mAccount).map(account -> return account.createUserIfNecessary(context); ).flatMap(account -> return Observable.from(account.getUserList().getUsers());

    ).filter(user -> return user.getProfiles().isEmpty(); ).flatMap(user -> return user.createNewProfile(); ).subscribe(onNext, onError, onCompleted);
  24. Solving MyMTV Observable.just(mAccount).map(account -> return account.createUserIfNecessary(context); ).flatMap(account -> return Observable.from(account.getUserList().getUsers());

    ).filter(user -> return user.getProfiles().isEmpty(); ).flatMap(user -> return user.createNewProfile(); ).subscribe(onNext, onError, onCompleted);
  25. Solving MyMTV Observable.just(mAccount).map(account -> return account.createUserIfNecessary(context); ).flatMap(account -> return Observable.from(account.getUserList().getUsers());

    ).filter(user -> return user.getProfiles().isEmpty(); ).flatMap(user -> return user.createNewProfile(); ).subscribe(onNext, onError, onCompleted);
  26. concat example // Our sources Observable<Data> memory = ...; Observable<Data>

    disk = ...; Observable<Data> network = ...; // Retrieve the first source with data Observable<Data> source = Observable .concat(memory, disk, network) .first();
  27. zip

  28. combineLatest example emailChangeObservable = RxTextView.textChangeEvents(email); passwordChangeObservable = RxTextView.textChangeEvents(password); submitButton.setEnabled(false); //

    force-disable the button Observable.combineLatest(emailChangeObservable, passwordChangeObservable,(emailObs, pwdObs) -> { boolean emailCheck = emailObs.text().length() >= 3; boolean passwordCheck = pwdObs.text().length() >= 3; return emailCheck && passwordCheck; }).subscribe(aBoolean -> { submitButton.setEnabled(aBoolean); }); // submit button will only be clickable if both forms have more than 3 characters each
  29. combineLatest example emailChangeObservable = RxTextView.textChangeEvents(email); passwordChangeObservable = RxTextView.textChangeEvents(password); submitButton.setEnabled(false); //

    force-disable the button Observable.combineLatest(emailChangeObservable, passwordChangeObservable,(emailObs, pwdObs) -> { boolean emailCheck = emailObs.text().length() >= 3; boolean passwordCheck = pwdObs.text().length() >= 3; return emailCheck && passwordCheck; }).subscribe(aBoolean -> { submitButton.setEnabled(aBoolean); }); // submit button will only be clickable if both forms have more than 3 characters each
  30. combineLatest example emailChangeObservable = RxTextView.textChangeEvents(email); passwordChangeObservable = RxTextView.textChangeEvents(password); submitButton.setEnabled(false); //

    force-disable the button Observable.combineLatest(emailChangeObservable, passwordChangeObservable,(emailObs, pwdObs) -> { boolean emailCheck = emailObs.text().length() >= 3; boolean passwordCheck = pwdObs.text().length() >= 3; return emailCheck && passwordCheck; }).subscribe(aBoolean -> { submitButton.setEnabled(aBoolean); }); // submit button will only be clickable if both forms have more than 3 characters each
  31. combineLatest example emailChangeObservable = RxTextView.textChangeEvents(email); passwordChangeObservable = RxTextView.textChangeEvents(password); submitButton.setEnabled(false); //

    force-disable the button Observable.combineLatest(emailChangeObservable, passwordChangeObservable,(emailObs, pwdObs) -> { boolean emailCheck = emailObs.text().length() >= 3; boolean passwordCheck = pwdObs.text().length() >= 3; return emailCheck && passwordCheck; }).subscribe(aBoolean -> { submitButton.setEnabled(aBoolean); }); // submit button will only be clickable if both forms have more than 3 characters each
  32. combineLatest example emailChangeObservable = RxTextView.textChangeEvents(email); passwordChangeObservable = RxTextView.textChangeEvents(password); submitButton.setEnabled(false); //

    force-disable the button Observable.combineLatest(emailChangeObservable, passwordChangeObservable,(emailObs, pwdObs) -> { boolean emailCheck = emailObs.text().length() >= 3; boolean passwordCheck = pwdObs.text().length() >= 3; return emailCheck && passwordCheck; }).subscribe(aBoolean -> { submitButton.setEnabled(aBoolean); }); // submit button will only be clickable if both forms have more than 3 characters each
  33. combineLatest example emailChangeObservable = RxTextView.textChangeEvents(email); passwordChangeObservable = RxTextView.textChangeEvents(password); submitButton.setEnabled(false); //

    force-disable the button Observable.combineLatest(emailChangeObservable, passwordChangeObservable,(emailObs, pwdObs) -> { boolean emailCheck = emailObs.text().length() >= 3; boolean passwordCheck = pwdObs.text().length() >= 3; return emailCheck && passwordCheck; }).subscribe(aBoolean -> { submitButton.setEnabled(aBoolean); }); // submit button will only be clickable if both forms have more than 3 characters each
  34. combineLatest example emailChangeObservable = RxTextView.textChangeEvents(email); passwordChangeObservable = RxTextView.textChangeEvents(password); submitButton.setEnabled(false); //

    force-disable the button Observable.combineLatest(emailChangeObservable, passwordChangeObservable,(emailObs, pwdObs) -> { boolean emailCheck = emailObs.text().length() >= 3; boolean passwordCheck = pwdObs.text().length() >= 3; return emailCheck && passwordCheck; }).subscribe(aBoolean -> { submitButton.setEnabled(aBoolean); }); // submit button will only be clickable if both forms have more than 3 characters each
  35. subscribeOn / observeOn <T> Transformer<T, T> applySchedulers() { return observable

    -> observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } Observable.from(someSource) .map(data -> manipulate(data)) .compose(applySchedulers()) .subscribe(data -> doSomething(data));
  36. subscribeOn / observeOn <T> Transformer<T, T> applySchedulers() { return observable

    -> observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()); } Observable.from(someSource) .map(data -> manipulate(data)) .compose(applySchedulers()) .subscribe(data -> doSomething(data));
  37. <T> Transformer<T, T> applySchedulers() { return observable -> observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread());

    } Observable.from(someSource) .map(data -> manipulate(data)) .compose(applySchedulers()) .subscribe(data -> doSomething(data)); subscribeOn / observeOn
  38. private Object slowBlockingMethod() { ... } public Observable<Object> newMethod() {

    return Observable.defer(() -> Observable.just(slowBlockingMethod())); } defer
  39. Cancelling subscriptions @Override public void onStart() { super.onStart(); mSubscriptions =

    new CompositeSubscription(); mSubscriptions.add(rxBus.toObserverable().subscribe(new Action1<Object>() { @Override public void call(Object event) { // ... }})); } @Override public void onStop() { super.onStop(); mSubscriptions.unsubscribe(); }
  40. Side effect operations observable.doOnSubscribe(action0); observable.doOnUnsubscribe(action0); observable.doOnNext(action1); observable.doOnCompleted(action0); observable.doOnError(action0); observable.doOnTerminate(action0); //

    before termination methods observable.finallyDo(action0); // after termination methods observable.doOnEach(action1); // next, completed or error observable.doOnRequest(action1);
  41. cache Observable<Photo> request = service.getUserPhoto(id).cache(); Subscription sub = request.subscribe(photo ->

    handleUserPhoto(photo)); // ...When the Activity is being recreated... sub.unsubscribe(); // ...Once the Activity is recreated... request.subscribe(photo -> handleUserPhoto(photo));