Slide 1

Slide 1 text

Reactive Programming with RxJava Duarte Nunes [email protected]

Slide 2

Slide 2 text

Agenda Functional reactive rx fundamental types operators Taming the Sequence concurrency

Slide 3

Slide 3 text

Reactive

Slide 4

Slide 4 text

Reactive re·ac·tive adjective \r ē - ˈ ak-tiv\ ! 1 of, relating to, or marked by reaction or reactance 2 readily responsive to a stimulus

Slide 5

Slide 5 text

Reactive re·ac·tive adjective \r ē - ˈ ak-tiv\ ! 1 of, relating to, or marked by reaction or reactance 2 readily responsive to a stimulus P

Slide 6

Slide 6 text

Reactive re·ac·tive adjective \r ē - ˈ ak-tiv\ ! 1 of, relating to, or marked by reaction or reactance 2 readily responsive to a stimulus P E

Slide 7

Slide 7 text

Reactive re·ac·tive adjective \r ē - ˈ ak-tiv\ ! 1 of, relating to, or marked by reaction or reactance 2 readily responsive to a stimulus P E

Slide 8

Slide 8 text

Reactive re·ac·tive adjective \r ē - ˈ ak-tiv\ ! 1 of, relating to, or marked by reaction or reactance 2 readily responsive to a stimulus P E REACTIVE

Slide 9

Slide 9 text

Reactive re·ac·tive adjective \r ē - ˈ ak-tiv\ ! 1 of, relating to, or marked by reaction or reactance 2 readily responsive to a stimulus Asynchronous Scalable Push VAlueS P E REACTIVE

Slide 10

Slide 10 text

Functional

Slide 11

Slide 11 text

Functional func·tion·al adjective \ ˈ f əŋ (k)-shn ə l, -sh ə -n ə l\ ! 1 of, relating to, or indicating mathematical functions 2 capable of functioning; working

Slide 12

Slide 12 text

Functional func·tion·al adjective \ ˈ f əŋ (k)-shn ə l, -sh ə -n ə l\ ! 1 of, relating to, or indicating mathematical functions 2 capable of functioning; working

Slide 13

Slide 13 text

Functional func·tion·al adjective \ ˈ f əŋ (k)-shn ə l, -sh ə -n ə l\ ! 1 of, relating to, or indicating mathematical functions 2 capable of functioning; working Functions HIGHER-ORDER (MOSTLY) PURe Composable

Slide 14

Slide 14 text

RxJava “a library for composing asynchronous and event-based programs using observable sequences for the Java VM" https://github.com/Netflix/RxJava A Java port of Rx https://github.com/Reactive-Extensions Reactive Extensions

Slide 15

Slide 15 text

Callbacks GWT AsyncCallback 1 service.getShapes(dbName, new AsyncCallback() { 2 def onSuccess(result: Array[Shape]): Unit = { /* ... */ } 3 def onFailure(caught: Throwable): Unit { /* ... */ } 4 });

Slide 16

Slide 16 text

Callbacks GWT AsyncCallback 1 service.getShapes(dbName, new AsyncCallback() { 2 def onSuccess(result: Array[Shape]): Unit = { /* ... */ } 3 def onFailure(caught: Throwable): Unit { /* ... */ } 4 }); one-shot No cancellation asynchronous computations are not values

Slide 17

Slide 17 text

Observers 1 trait Observer[-A] { 2 def onNext(value: A): Unit = {} 3 def onError(error: Throwable): Unit= {} 4 def onCompleted(): Unit = {} 5 }

Slide 18

Slide 18 text

Futures? 1 trait Future[+A] { 2 def onComplete[B](f: Try[A] => B): Unit 3 def isCompleted: Boolean 4 def value: Option[Try[A]] 5 }

Slide 19

Slide 19 text

Futures? 1 trait Future[+A] { 2 def onComplete[B](f: Try[A] => B): Unit 3 def isCompleted: Boolean 4 def value: Option[Try[A]] 5 } asynchronously stream back results Goal

Slide 20

Slide 20 text

Futures? 1 trait Future[+A] { 2 def onComplete[B](f: Try[A] => B): Unit 3 def isCompleted: Boolean 4 def value: Option[Try[A]] 5 } asynchronously stream back results Goal 1 trait Future[+A] { 2 def subscribe(observer: Observer[A]): Unit 3 def tryCancel(): Boolean 4 }

Slide 21

Slide 21 text

Observables 1 trait Observable[+T] { 2 def subscribe(observer: Observer[T]): Subscription 3 }

Slide 22

Slide 22 text

Duality ITERATOR OBSERVER def next(): A throws Throwable def hasNext(): Boolean def onNext(a: A): Unit def onError(t: Throwable): Unit def onCompleted(): Unit ITERATABLE OBSERVABLE def iterator: Iterator[A] def subscribe(o: Observer[A]): Subscription getDataFromMemory() drop(10) take(5) map { _ + "_transformed" } foreach println getDataFromNetwork() drop(10) take(5) map { _ + "_transformed" } subscribe println

Slide 23

Slide 23 text

Interacting with data Async SYNC Single Multiple A Iterable[A] Observable[A] Future[A]

Slide 24

Slide 24 text

Observables are… produced & consumed with side-effects sequences of data in motion! composed functionally synchronous or asynchronous

Slide 25

Slide 25 text

Concurrency is abstracted def subcribe(observer: Observer[A]): Subscription Calling thread callback thread Calling thread

Slide 26

Slide 26 text

Concurrency is abstracted Calling thread callback threads thread pool def subcribe(observer: Observer[A]): Subscription

Slide 27

Slide 27 text

Concurrency is abstracted Calling thread callback thread Actor def subcribe(observer: Observer[A]): Subscription

Slide 28

Slide 28 text

Concurrency is abstracted Calling thread callback thread nio Event loop def subcribe(observer: Observer[A]): Subscription

Slide 29

Slide 29 text

Contract OnNext(t)* (OnCompleted() | OnError(e))? after zero or more OnNext calls, either one of OnCompleted or OnError will optionally be called observers can assume to be synchronised conceptually they run under a lock, similar to the reactor pattern Observable.synchronize UNsubscribing May not cancel Outsanding work work already in progress might still complete as it is not always safe to abort work in progress, but should not be signaled to unsubscribed observers

Slide 30

Slide 30 text

Observable.create 1 def create(subscribe: Observer[A] => Subscription): Observable[A] 2 3 Observable.create(observer => { 4 try { 5 observer.onNext(...) 6 observer.onCompleted(); 7 } catch(Exception e) { 8 observer.onError(e); 9 } 10 }) x

Slide 31

Slide 31 text

Interval interval( ) …

Slide 32

Slide 32 text

Interval interval( ) … Cold Observable

Slide 33

Slide 33 text

1 def myInterval(d: Duration): Observable[Long] = { 2 val t = new Timer(true) 3 Observable.create(observer => { 4 @volatile var done = false 5 var c = 0l 6 def createTimerTask() = new TimerTask() { 7 def run() { 8 if (done) return 9 observer.onNext(c) 10 c += 1 11 t.schedule(createTimerTask(), d.toMillis) 12 }} 13 t.schedule(createTimerTask(), d.toMillis) 14 Subscription { done = true; } 15 }) 16 }

Slide 34

Slide 34 text

Subscriptions life-cycle management unsubscribe unsubscribe unsubscribe subscription boolean Composite refcount multiassignment serial

Slide 35

Slide 35 text

Subjects onXXX onXXX replaysubject behavioursubject caches values remembers last publication requires default value asyncsubject caches the last value waits until sequence is over

Slide 36

Slide 36 text

From future 1 def from[T](f: Future[T])(implicit ec: ExecutionContext): 2 Observable[T] = { 3 val s = AsyncSubject[T]() 4 f.onComplete { 5 case Success(c) => 6 s.onNext(c) 7 s.onCompleted() 8 case Failure(e) => 9 s.onError(e) 10 } 11 s 12 } x

Slide 37

Slide 37 text

assume 1 implicit class ObservableOps[A](val o: Observable[A]) extends AnyVal { 2 // operator definitions go here 3 } 4 5 Observable.interval(200 millis) take(3) filter (isEven _)

Slide 38

Slide 38 text

Take take(3)

Slide 39

Slide 39 text

1 def myTake(n: Int): Observable[A] = 2 Observable.create(observer => { 3 var c = n 4 var s = CompositeSubscription() 5 s += o.subscribe( 6 ((a: A) => { 7 if (c > 0) { 8 observer.onNext(a) 9 c -= 1 10 if (c == 0) { 11 s.unsubscribe() 12 observer.onCompleted() 13 } 14 } 15 }), 16 observer.onError _, 17 observer.onCompleted _) 18 s 19 })

Slide 40

Slide 40 text

Filter x x 1 def myFilter(p: A => Boolean): Observable[A] = 2 Observable.create(observer => { 3 o.subscribe( 4 (a: A) => { 5 if (p(a)) 6 observer.onNext(a) 7 }, 8 observer.onError _, 9 observer.onCompleted _) 10 }) filter(isEven)

Slide 41

Slide 41 text

Any = true = false any() any()

Slide 42

Slide 42 text

1 def myAny(): Observable[Boolean] = 2 Observable.create(observer => { 3 var s = CompositeSubscription() 4 var hasReturned = false 5 s += o.subscribe( 6 (_: A) => { 7 if (!hasReturned) { 8 hasReturned = true 9 observer.onNext(true) 10 observer.onCompleted() 11 s.unsubscribe 12 } 13 }, 14 observer.onError _, 15 () => { 16 if (!hasReturned) { 17 observer.onNext(false) 18 observer.onCompleted() 19 } 20 }) 21 s 22 })

Slide 43

Slide 43 text

FoldLeft 1 def myFoldLeft[B](z: B)(f: (B, A) => B): Observable[B] = 2 Observable.create(observer => { 3 var acc = z 4 o.subscribe( 5 (a: A) => acc = f(acc, a), 6 observer.onError _, 7 () => { 8 observer.onNext(acc) 9 observer.onCompleted() 10 }) 11 }) foldLeft(0)(_ + _)

Slide 44

Slide 44 text

FoldLeft 1 def myFoldLeft[B](z: B)(f: (B, A) => B): Observable[B] = 2 Observable.create(observer => { 3 var acc = z 4 o.subscribe( 5 (a: A) => acc = f(acc, a), 6 observer.onError _, 7 () => { 8 observer.onNext(acc) 9 observer.onCompleted() 10 }) 11 }) methods like single() force a result but are blocking Careful foldLeft(0)(_ + _)

Slide 45

Slide 45 text

FlatMap flatMap { }

Slide 46

Slide 46 text

1 def flatMap[R](f: T => Observable[R]): Observable[R] 2 3 4 def filter(predicate: A => Boolean): Observable[A] = 5 o flatMap { x => 6 if (predicate(x)) 7 Observable.items(x); 8 else 9 Observable.empty[A](); 10 } 11 12 def map[R](func: T => R): Observable[R] = { 13 o flatMap (func andThen Obsersable.items) 14 }

Slide 47

Slide 47 text

Anamorphisms a => observable[a] Catamorphisms Bind / flatMap observable[a] => B observable[a] => Observable[b]

Slide 48

Slide 48 text

Side-effects

Slide 49

Slide 49 text

Side-effects compose data in the pipeline

Slide 50

Slide 50 text

Side-effects compose data in the pipeline use map to introduce state

Slide 51

Slide 51 text

Side-effects compose data in the pipeline sometimes side-effects are unavoidable use map to introduce state

Slide 52

Slide 52 text

Side-effects compose data in the pipeline sometimes side-effects are unavoidable use map to introduce state def doOnNext(onNext: A => Unit): Observable[A]

Slide 53

Slide 53 text

Side-effects compose data in the pipeline sometimes side-effects are unavoidable beware of mutable elements use map to introduce state def doOnNext(onNext: A => Unit): Observable[A]

Slide 54

Slide 54 text

Observable .toBlockingObservable Leaving the monad 1 def foreach(f: A => Unit): Unit 2 3 def withFilter(p: A => Boolean): WithFilter[A] 4 5 def toIterable: Iterable[A] 6 7 def single: A 8 9 def singleOption: Option[A] 10 11 def toFuture: Future[A]

Slide 55

Slide 55 text

Error Handling

Slide 56

Slide 56 text

Error Handling 1 def retry(retryCount: Int): Observable[T] retry

Slide 57

Slide 57 text

Error Handling 1 def retry(retryCount: Int): Observable[T] retry onErrorResumeNext { } x

Slide 58

Slide 58 text

Combining Sequences concat

Slide 59

Slide 59 text

Combining Sequences concat sequential concat repeat startwith

Slide 60

Slide 60 text

Combining Sequences merge x x

Slide 61

Slide 61 text

Combining Sequences merge concurrent amb switch zip merge x x

Slide 62

Slide 62 text

1 def myMerge(other: Observable[A]): Observable[A] = 2 Observable.create(observer => { 3 val s = CompositeSubscription() 4 val obs = createObserver(s) 5 s += o.subscribe(obs) 6 s += other.subscribe(obs) 7 s 8 }) 1 def createObserver(s: Subscription): Observer[A] = { 2 var done = false 3 Observer( 4 (a: A) => { 5 synchronized 6 if (!done) 7 observer.onNext(a) 8 }, t => { 9 synchronized 10 if (!done) { 11 done = true; s.unsubscribe; observer.onError(t) 12 } 13 }, () => { 14 synchronized 15 if (!done) { 16 done = true; s.unsubscribe; observer.onCompleted() 17 } 18 }) 19 }

Slide 63

Slide 63 text

switch 1 def switch[U](implicit evidence: Observable[T] <:< Observable[Observable[U]]): Observable[U] switch

Slide 64

Slide 64 text

An example switch observeChanges(output) map(word => completions(word)) throttle(10 millis) textChanges(input)

Slide 65

Slide 65 text

time axis is an unknown

Slide 66

Slide 66 text

time axis is an unknown Time shifted sequences maps data on the time plane Buffer Delay Sample Throttle Timeout

Slide 67

Slide 67 text

time axis is an unknown Time shifted sequences maps data on the time plane Buffer Delay Sample sequences of coincidence Throttle Timeout Buffer Join Window sliding windows

Slide 68

Slide 68 text

Sharing Observable

Slide 69

Slide 69 text

Sharing publish Observable ConnectableObservable

Slide 70

Slide 70 text

Sharing Subject publish Observable ConnectableObservable

Slide 71

Slide 71 text

Sharing Subject publish Observable ConnectableObservable

Slide 72

Slide 72 text

Sharing Subject publish connect / refCount Observable ConnectableObservable

Slide 73

Slide 73 text

Sharing Subject publish connect / refCount Observable ConnectableObservable

Slide 74

Slide 74 text

Sharing Subject publish connect / refCount Observable ConnectableObservable

Slide 75

Slide 75 text

Sharing Subject publish connect / refCount 1 val o = Observable.interval(200 millis) 2 val c: ConnectableObservable[Long] = o.publish 3 c.subscribe(x => println(x)) 4 val s = c.connect 5 c.subscribe(x => println(x)) 6 s.unsubscribe Observable ConnectableObservable

Slide 76

Slide 76 text

from iterable infinity and beyond? 1 def myFrom[A](xs: Iterable[A]): Observable[A] = 2 Observable.create(observer => { 3 xs foreach (observer.onNext _) 4 observer.onCompleted() 5 Subscription { } 6 })

Slide 77

Slide 77 text

Concurrency 1 object Future { 2 def apply[A](body: =>T)(implicit executor: ExecutionContext): Future[A] 3 }

Slide 78

Slide 78 text

Concurrency 1 object Future { 2 def apply[A](body: =>T)(implicit executor: ExecutionContext): Future[A] 3 } 1 trait Observable[A] { 2 def observeOn(scheduler: Scheduler): Observable[A] 3 }

Slide 79

Slide 79 text

Concurrency 1 object Future { 2 def apply[A](body: =>T)(implicit executor: ExecutionContext): Future[A] 3 } 1 trait Observable[A] { 2 def observeOn(scheduler: Scheduler): Observable[A] 3 }

Slide 80

Slide 80 text

Schedulers 1 trait Scheduler { 2 def schedule(action: => Unit): Subscription 3 def schedule(action: Scheduler => Subscription): Subscription 4 def scheduleRec(work: (=>Unit) => Unit): Subscription 5 // ... 6 }

Slide 81

Slide 81 text

from iterable 1 def myFrom2[A](xs: Iterable[A], s: Scheduler): Observable[A] = 2 Observable.create(observer => { 3 s schedule { 4 xs foreach (observer.onNext _) 5 observer.onCompleted() 6 } 7 }) again

Slide 82

Slide 82 text

from iterable 1 def myFrom2[A](xs: Iterable[A], s: Scheduler): Observable[A] = 2 Observable.create(observer => { 3 s schedule { 4 xs foreach (observer.onNext _) 5 observer.onCompleted() 6 } 7 }) still Wrong again

Slide 83

Slide 83 text

from iterable again 1 def myFrom2[A](xs: Iterable[A], s: Scheduler): Observable[A] = 2 Observable.create(observer => { 3 s schedule { 4 xs foreach (observer.onNext _) 5 observer.onCompleted() 6 } 7 }) still Wrong again and

Slide 84

Slide 84 text

from iterable again 1 def myFrom2[A](xs: Iterable[A], s: Scheduler): Observable[A] = 2 Observable.create(observer => { 3 s schedule { 4 xs foreach (observer.onNext _) 5 observer.onCompleted() 6 } 7 }) still Wrong 1 def myFrom3[A](xs: Iterable[A], s: Scheduler): Observable[A] = 2 Observable.create(observer => { 3 val it = xs.iterator 4 s.scheduleRec(self => { 5 if (it.hasNext) { 6 observer.onNext(it.next()) 7 self 8 } else observer.onCompleted() 9 })}) again and

Slide 85

Slide 85 text

R 1 implicit class SchedulerOps(val s: Scheduler) extends AnyVal { 2 3 def mySchedule(work: (=>Unit) => Unit): Subscription = 4 s.schedule(scheduler => { 5 6 val subscription = MultipleAssignmentSubscription() 7 8 def loop(): Unit = { 9 subscription.subscription = scheduler.schedule { 10 work { loop() } 11 } 12 } 13 14 loop() 15 16 subscription 17 }) 18 } ecursive scheduling

Slide 86

Slide 86 text

Thank you Questions?