(2:40) RX is about programming with asynchronous data streams. These streams can come from anywhere: user click events, push notifications and GPS updates for example.

(4:45) You can use RX in many places. Even if there’s only one event (e.g. a response from a network request), many events (e.g. user typing in a text field), or an error (such that you get zero data events) — they can all be modelled as asynchronous data streams.

(6:15) RX is a tool that facilitates lots of things, including:

chaining — start operations based on the result of an earlier operation

threading — schedule work and delivery of events on different threads without much effort

composition & data transformation — combine smaller functions together to achieve goals

(8:45) It’s similar to code you’ll see in Kotlin. By default, Kotlin operators work synchronously on finite data sets.

This example in Kotlin demonstrates some standard operators. Each line results in a new collection being created.

Conversely, RX waits until there’s a subscriber, and applies operations on each emission as and when they come (“lazy”).

In Kotlin, you can use asSequence() to transform any Array or Iterable into a Sequence, making the operations “lazy”. Here, the operations will not be applied until a terminal operator (e.g. toList) is called:

Each Sequence operator wraps the output of the previous Sequence operator in a new layer. You can read more about Sequences here.

(13:30) An observable exposes an event stream from some producer of events.

(13:42) Hot vs cold observables. I found this section a little jarring because it was at odds with my understanding of these terms.

There’s more reading from Ben Lesh here.

A cold observable is one which creates (or activates) a sequence of events in its onSubscribe() function. For each observer that subscribes to it, a new stream of events will be sent.

A hot observable is one that wraps around a producer that is created/activated potentially before an observer has subscribed to it. By the time an observer has subscribed, the producer might already have started emitting items or already terminated.

(15:15) Creating observables can be done with factory methods on the Observable class.

Here’s an example which emits the values 5 , 6 , and 7 then finishes:

This is an example of a cold observable. None of the statements inside the block will be executed until a subscriber subscribes. For each subscriber that subscribes, they’ll receive their own stream of 5 , 6 , and 7 events followed by an onComplete() .

(18:29) Observers are an abstraction for a consumer of observable events.

(20:02) The standard observer lifecycle starts with a call to observer.onSubscribe(...) when it calls observable.subscribe(observer) .

From the subscribed state, it can receive multiple calls to observer.onNext(t) which are emissions from the observable, or transition into a terminal state with observer.onError(e) or observer.onComplete() .

(21:05) A simpler way to consume the output of an observable is to use Consumer<T> . This only has one method, fun accept(t: T) which is like Observer.onNext(t: T) .

The IDE will give us a hint that this anonymous class can be converted into a lambda:

Since Kotlin has type inference, we don’t even need to specify that it’s a Consumer<Int> (but be wary of clashing methods — see this post by Victoria Gonda):

Where you have a lambda as the last parameter in a method, you can put the closure outside the parentheses.

If there are no other parameters, then you can get rid of the parentheses:

If there’s only one method in the lambda, and it only has a single parameter, then you can use it as an alias for that parameter:

Nicole Borelli wrote a great post about this shorthand lambda syntax!

(23:48) Operators can be used to create, transform, filter and chain observables. We’ll look at the map() operator. This transforms a stream of one type of item to a stream of another type of item.

This will emit the items 5 , 6 and 7 , transforming each integer into a string (of “hello ” repeated that many times), and then print the three strings:

hello hello hello hello hello

hello hello hello hello hello hello

hello hello hello hello hello hello hello

Written in its expanded form it looks like this:

where these two are the same:

Sometimes it’s easier to break out the lambdas and shorthand as above to help understand long pipelines, especially as you’re getting used to it.

(27:24) The flatMap() operator transforms an item into an observable of (potentially) a different item.

(27:55) Consider you’re building a blogging platform and you want to display a list of all posts from each user.

Matilda has written 3 articles, Jenny 2 and Agatha just 1

You have available to you:

If we used map() , we could transform our stream of User into a stream of List<Article> where each list would be the articles written by a given user:

Marble diagram shows three emissions, Matilda, Agatha and Jenny being transformed into three emissions of type List<Article>

But we don’t want Observable<List<Article>> , we want Observable<Article> .

We can use flatMap() to turn this stream of User into a stream of Article :

Marble diagram shows three emissions, Matilda, Agatha and Jenny being transformed into a single stream of 6 articles

(29:50) So, should you use RX? Well, do you:

like functional programming?

need asynchronous processing of items?

need to compose data from multiple sources?

want to handle errors gracefully?

If you said “yes” to most of these, then “it truly does depend”.

RX might be too complex for your team or for what you’re building, especially if you’re not familiar with it. There’s other options available to you.

On the other hand, it’s very pervasive — it’s available in multiple languages, and many platforms, with lots of examples abound.

(30:50) Recap of operators, observers and observables.

Thanks Annyce!

Thanks also to Toto for helping with the hot/cold definition.