Slide 1

Slide 1 text

Rx Reactive Extensions 22 décembre 2015

Slide 2

Slide 2 text

Problems to solve ● Callback hell

Slide 3

Slide 3 text

Problems to solve ● Callback hell ● Chaining operations

Slide 4

Slide 4 text

Problems to solve ● Callback hell ● Chaining operations

Slide 5

Slide 5 text

Problems to solve ● Callback hell ● Chaining operations ● Threading is hard

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

Problems to solve ● Callback hell ● Chaining operations ● Threading is hard ● Error handling

Slide 8

Slide 8 text

Example: MyMTV Login Account User User User User User Profile Profile Profile Profile Profile Success!

Slide 9

Slide 9 text

Example: RMN Slideshow: - collection of medias (images and videos) - images should be displayed as such - videos should display a preview

Slide 10

Slide 10 text

Reactive Extensions

Slide 11

Slide 11 text

Rx: Reactive Extensions ● Functional Reactive Programming : 1997 (40 years after Functional/OOP) ● Started by Microsoft for .NET ● RxJava: 2012 (Netflix) => ReactiveCocoa, RxAndroid

Slide 12

Slide 12 text

Observable Pattern

Slide 13

Slide 13 text

Observable single items multiple items synchronous T getData() Iterable getData() Collection getData() asynchronous Future getData() Observable getData()

Slide 14

Slide 14 text

Subscriber event Iterable (pull) Observable (push) retrieve data T next() onNext(T) discover error throws Exception onError(Exception) complete returns onCompleted()

Slide 15

Slide 15 text

Observable Pattern

Slide 16

Slide 16 text

Creating an Observable Observable o = Observable.from("a", "b", "c"); def list = [5, 6, 7, 8] Observable o = Observable.from(list); Observable o = Observable.just("one object");

Slide 17

Slide 17 text

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);

Slide 18

Slide 18 text

Creating an Observable func myJust(element: E) -> Observable { return create { observer in observer.on(.Next(element)) observer.on(.Completed) return NopDisposable.instance } } myJust(0) .subscribeNext { n in print(n) }

Slide 19

Slide 19 text

“Warmth” of an Observable

Slide 20

Slide 20 text

Operators

Slide 21

Slide 21 text

Observable Pattern

Slide 22

Slide 22 text

Visualising operators

Slide 23

Slide 23 text

map

Slide 24

Slide 24 text

flatMap

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

filter

Slide 31

Slide 31 text

Solving MyMTV Login Account User User User User User Profile Profile Profile Profile Profile Success!

Slide 32

Slide 32 text

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);

Slide 33

Slide 33 text

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);

Slide 34

Slide 34 text

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);

Slide 35

Slide 35 text

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);

Slide 36

Slide 36 text

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);

Slide 37

Slide 37 text

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);

Slide 38

Slide 38 text

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);

Slide 39

Slide 39 text

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);

Slide 40

Slide 40 text

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);

Slide 41

Slide 41 text

merge

Slide 42

Slide 42 text

merge vs concat

Slide 43

Slide 43 text

concat example // Our sources Observable memory = ...; Observable disk = ...; Observable network = ...; // Retrieve the first source with data Observable source = Observable .concat(memory, disk, network) .first();

Slide 44

Slide 44 text

flatMap vs concatMap

Slide 45

Slide 45 text

switchMap

Slide 46

Slide 46 text

skip

Slide 47

Slide 47 text

distinct

Slide 48

Slide 48 text

distinctUntilChanged

Slide 49

Slide 49 text

retryWhen

Slide 50

Slide 50 text

defaultIfEmpty - switchIfEmpty

Slide 51

Slide 51 text

debounce

Slide 52

Slide 52 text

Text field -> search RxTextView.textChanges(searchEditText) .debounce(150, MILLISECONDS) .switchMap(Api::searchItems) .retryWhen(new RetryWithConnectivityIncremental(context, 5, 15, SECONDS)) .subscribe(updateList, t->showErrorToUser());

Slide 53

Slide 53 text

Text field -> search RxTextView.textChanges(searchEditText) .debounce(150, MILLISECONDS) .switchMap(Api::searchItems) .retryWhen(new RetryWithConnectivityIncremental(context, 5, 15, SECONDS)) .subscribe(updateList, t->showErrorToUser());

Slide 54

Slide 54 text

Text field -> search RxTextView.textChanges(searchEditText) .debounce(150, MILLISECONDS) .switchMap(Api::searchItems) .retryWhen(new RetryWithConnectivityIncremental(context, 5, 15, SECONDS)) .subscribe(updateList, t->showErrorToUser());

Slide 55

Slide 55 text

debounce

Slide 56

Slide 56 text

Text field -> search RxTextView.textChanges(searchEditText) .debounce(150, MILLISECONDS) .switchMap(Api::searchItems) .retryWhen(new RetryWithConnectivityIncremental(context, 5, 15, SECONDS)) .subscribe(updateList, t->showErrorToUser());

Slide 57

Slide 57 text

Text field -> search RxTextView.textChanges(searchEditText) .debounce(150, MILLISECONDS) .switchMap(Api::searchItems) .retryWhen(new RetryWithConnectivityIncremental(context, 5, 15, SECONDS)) .subscribe(updateList, t->showErrorToUser());

Slide 58

Slide 58 text

switchMap - switchOnNext

Slide 59

Slide 59 text

Text field -> search RxTextView.textChanges(searchEditText) .debounce(150, MILLISECONDS) .switchMap(Api::searchItems) .retryWhen(new RetryWithConnectivityIncremental(context, 5, 15, SECONDS)) .subscribe(updateList, t->showErrorToUser());

Slide 60

Slide 60 text

Text field -> search RxTextView.textChanges(searchEditText) .debounce(150, MILLISECONDS) .switchMap(Api::searchItems) .retryWhen(new RetryWithConnectivityIncremental(context, 5, 15, SECONDS)) .subscribe(updateList, t->showErrorToUser());

Slide 61

Slide 61 text

retryWhen

Slide 62

Slide 62 text

Text field -> search RxTextView.textChanges(searchEditText) .debounce(150, MILLISECONDS) .switchMap(Api::searchItems) .retryWhen(new RetryWithConnectivityIncremental(context, 5, 15, SECONDS)) .subscribe(updateList, t->showErrorToUser());

Slide 63

Slide 63 text

Text field -> search RxTextView.textChanges(searchEditText) .debounce(150, MILLISECONDS) .switchMap(Api::searchItems) .retryWhen(new RetryWithConnectivityIncremental(context, 5, 15, SECONDS)) .subscribe(updateList, t->showErrorToUser());

Slide 64

Slide 64 text

Text field -> search RxTextView.textChanges(searchEditText) .debounce(150, MILLISECONDS) .switchMap(Api::searchItems) .retryWhen(new RetryWithConnectivityIncremental(context, 5, 15, SECONDS)) .subscribe(updateList, t->showErrorToUser());

Slide 65

Slide 65 text

timeout

Slide 66

Slide 66 text

zip

Slide 67

Slide 67 text

combineLatest

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

Threading (and more)

Slide 76

Slide 76 text

subscribeOn / observeOn myObservableServices.retrieveImage(url) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(bitmap -> myImageView.setImageBitmap(bitmap));

Slide 77

Slide 77 text

subscribeOn / observeOn

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

Transformer 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

Slide 81

Slide 81 text

private Object slowBlockingMethod() { ... } public Observable newMethod() { return Observable.defer(() -> Observable.just(slowBlockingMethod())); } defer

Slide 82

Slide 82 text

Cancelling subscriptions @Override public void onStart() { super.onStart(); mSubscriptions = new CompositeSubscription(); mSubscriptions.add(rxBus.toObserverable().subscribe(new Action1() { @Override public void call(Object event) { // ... }})); } @Override public void onStop() { super.onStop(); mSubscriptions.unsubscribe(); }

Slide 83

Slide 83 text

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);

Slide 84

Slide 84 text

cache

Slide 85

Slide 85 text

cache Observable 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));

Slide 86

Slide 86 text

cache

Slide 87

Slide 87 text

publish

Slide 88

Slide 88 text

back pressure

Slide 89

Slide 89 text

Questions? 22 décembre 2015