Share
Embed
Why Reactive Programming?
An introduction to Reactive Programming focused on Android.
The sample codes are written in RxJava with Kotlin.
Slides at Android Dev Conference BR 2017.
Felipe Costa
August 25, 2017
Transcript
78 slides
-
Why Reactive Programming? Felipe Costa Android Developer @ OLX Brasil
Bruno Kosawa Android Developer @ OLX Brasil -
OLX is part of the biggest online marketplace group in
the world Why Reactive Programming? About OLX Brasil One of the biggest groups on Internet1 One of the biggest groups of online classified2 1 USDbn mkt cap, Fev/2017 2 EURm receita classificados, 2015 -
OLX is the result of two big companies Why Reactive
Programming? About OLX Brasil 50% 50% + = Shareholders -
Why Reactive Programming? About the Speakers • Bacharel em Ciência
da Computação pela UFRJ • Trabalha com programação desde 2011 • Trabalha com Android desde 2014 • Engenheiro de software na OLX há 1 ano • Bacharel em Ciência da Computação pela UFRJ • Trabalha com programação desde 2010 • Trabalha com Android desde 2011 • Engenheiro de software na OLX há 2 anos Felipe Costa Bruno Kosawa -
Counter function fun count(array: Array<String>): Int { var count =
0 for (item in array) count++ return count } Why Reactive Programming? 1. Input and Output -
Kinds of inputs we deal with Why Reactive Programming? //
Listeners View.OnClickListener { view -> /* */ } // Lifecycle Callbacks override fun onCreate(savedInstanceState: Bundle?) { /* */ } override fun onBindViewHolder(holder: ViewHolder, position: Int) { /* */ } // Async Callbacks override fun onFailure(throwable: Throwable) { /* */ } override fun onResponse(response: ReponseDTO) { /* */ } 1. Input and Output -
Why Reactive Programming? 1. Input and Output override fun onCreate(savedInstanceState:
Bundle?, persistentState: PersistableBundle?) { super.onCreate(savedInstanceState, persistentState) setupInitialState() Api.loadCharacters(object : ResponseCallback<List<Character>> { override fun onResponse(response: List<Character>) { updateUI(response) } override fun onFailure(throwable: Throwable) { showLoadCharactersError() } }) button.setOnClickListener { Api.favoriteCharacter(object : ResponseCallback<Character> { override fun onResponse(response: Character) { updateFavoriteUI(response) } override fun onFailure(throwable: Throwable) { handleFavoriteError() } }) } } Execution Order Framework Network Network UI Time -
Imperative Programming Why Reactive Programming? 1. Input and Output Framework
Network UI Others Execution Order Time Update State Know what happened previously -
State is exponentially complex Why Reactive Programming? val isDisplayed: Boolean
// 2 states val isEnabled: Boolean // 4 states val isSelected: Boolean // 8 states val isFavored: Boolean // 16 states 1. Input and Output -
Why Reactive Programming? 1. Input and Output Compositional Event System
(Non-Linear Programming) Imperative Programming -
• Paper 1989 - Gérard Berry • Microsoft Rx (2009)
• Rx = Reactive Extensions • RxJava (Kotlin S2) • “Functional Reactive Programming” A little bit of context Why Reactive Programming? 2. Definition -
We all responde to flowing data and react to changes…
Broadcasts Why Reactive Programming? 2. Definition Listeners MVP MVC Callbacks Event Bus -
What is reactive programming? It is programming paradigm oriented by
data streams and the propagation of change Why Reactive Programming? 2. Definition -
Propagation of Change Having α := ß + γ with
ß = 10 and γ = 20 Why Reactive Programming? 3. Propagation of change Spreadsheet ß γ α 10 20 30 -
Propagation of Change When ß changes to 30, α changes
as well Why Reactive Programming? 3. Propagation of change Spreadsheet ß γ α 30 20 50 -
Observer Pattern Why Reactive Programming? 3. Propagation of change Observer
+notify() ConcreteObserver2 +notify() ConcreteObserver1 +notify() Subject +registerObserver(observer) +unregisterObserver(observer) +notifyObservers() +observerCollection -
Observable Why Reactive Programming? 3. Propagation of change • Container
that can be emit signals to observers • Signals are emitted through time -
Data Stream Values over time. Examples: • Mouse cursor positions
on the screen; • Property values updates; • Text Field changes; • Bits being downloaded from the cloud Why Reactive Programming? 4. Data streams -
Why Reactive Programming? 4. Data streams just("Alpha", "Beta", "Gamma").subscribe({ it
-> println(it)}) Time Gamma Beta Alpha -
Why Reactive Programming? 4. Data streams just("Alpha", "Beta", "Gamma") .subscribe({
it -> println(it)}, { println("completed")}) Time Gamma Beta Alpha onCompleted -
Why Reactive Programming? 4. Data streams just("Alpha", "Beta", "Gamma") .subscribe({
it -> println(it)}, { it -> println(it.message)}, { println("completed")}) Time Gamma Beta Alpha onError -
Time Why Reactive Programming? 5. Data streams are composable Time
Observable.transform(f: (x) -> y): Observable -
Time Why Reactive Programming? 5. Data streams are composable 5
2 8 1 Time 2 16 4 10 map { it * 2 } -
Time Why Reactive Programming? 5. Data streams are composable 5
2 8 1 Time 8 2 filter { it % 2 == 0 } -
Why Reactive Programming? 5. Data streams are composable var lastClickTime:
Long = 0 override fun onClick(v: View) { val clickTime = System.currentTimeMillis() if (clickTime - lastClickTime < 700L) { onDoubleClick(v) lastClickTime = 0 } else { onSingleClick(v) } lastClickTime = clickTime } fun onSingleClick(v: View) { /* */ } fun onDoubleClick(v: View) { /* */ } Double Tap -
Why Reactive Programming? 5. Data streams are composable container.clicks() .buffer(700L,
TimeUnit.MILLISECONDS) .map { it.size } .map { if (it == 2) "Double Tap" else "" } .subscribe(textView::setText) Double Tap -
Why Reactive Programming? 5. Data streams are composable container.clicks() .buffer(700L,
TimeUnit.MILLISECONDS) .map { it.size } .map { if (it == 2) "Double Tap" else "" } .subscribe(textView::setText) Double Tap Time -
Why Reactive Programming? 5. Data streams are composable container.clicks() .buffer(700L,
TimeUnit.MILLISECONDS) .map { it.size } .map { if (it == 2) "Double Tap" else "" } .subscribe(textView::setText) Double Tap Time Time Buffer(700L) -
Why Reactive Programming? 5. Data streams are composable container.clicks() .buffer(700L,
TimeUnit.MILLISECONDS) .map { it.size } .map { if (it == 2) "Double Tap" else "" } .subscribe(textView::setText) Double Tap Time Time Map { it.size } 1 2 1 3 -
Why Reactive Programming? 5. Data streams are composable container.clicks() .buffer(700L,
TimeUnit.MILLISECONDS) .map { it.size } .map { if (it == 2) "Double Tap" else "" } .subscribe(textView::setText) Double Tap Time Time map { if (it == 2) "Double Tap" else "" } DT 1 2 1 3 -
Why Reactive Programming? 5. Data streams are composable container.clicks() .buffer(700L,
TimeUnit.MILLISECONDS) .map { it.size } .map { if (it == 2) "Double Tap" else "" } .subscribe(textView::setText) Double Tap -
Why Reactive Programming? 5. Data streams are composable container.clicks() .buffer(700L,
TimeUnit.MILLISECONDS) .map { it.size } .filter { it >= 2 } Multiple Tap -
Why Reactive Programming? 5. Data streams are composable var counter
= 0 upButton.setOnClickListener { counter++ resultTextView.text = counter.toString() } downButton.setOnClickListener { counter-- resultTextView.text = counter.toString() } Up and Down -
Why Reactive Programming? 5. Data streams are composable val upStream
= upButton.clicks().map { 1 } val downStream = downButton.clicks().map { -1 } merge(upStream, downStream) .startWith(0) .scan({ t1, t2 -> t1 + t2 }) .map { it.toString() } .subscribe(resultTextView::setText) Up and Down -
Why Reactive Programming? 5. Data streams are composable val upStream
= upButton.clicks().map { 1 } val downStream = downButton.clicks().map { -1 } merge(upStream, downStream) .startWith(0) .scan({ t1, t2 -> t1 + t2 }) .map { it.toString() } .subscribe(resultTextView::setText) Up and Down -
Why Reactive Programming? 5. Data streams are composable val upStream
= upButton.clicks().map { 1 } val downStream = downButton.clicks().map { -1 } merge(upStream, downStream) .startWith(0) .scan({ t1, t2 -> t1 + t2 }) .map { it.toString() } .subscribe(resultTextView::setText) Up and Down -
Why Reactive Programming? 5. Data streams are composable val upStream
= upButton.clicks().map { 1 } val downStream = downButton.clicks().map { -1 } merge(upStream, downStream) .startWith(0) .scan({ t1, t2 -> t1 + t2 }) .map { it.toString() } .subscribe(resultTextView::setText) Up and Down -
Why Reactive Programming? 5. Data streams are composable val upStream
= upButton.clicks().map { 1 } val downStream = downButton.clicks().map { -1 } merge(upStream, downStream) .startWith(0) .scan({ t1, t2 -> t1 + t2 }) .map { it.toString() } .subscribe(resultTextView::setText) Up and Down -
Why Reactive Programming? 5. Data streams are composable val upStream
= upButton.clicks().map { 1 } val downStream = downButton.clicks().map { -1 } merge(upStream, downStream) .startWith(0) .scan({ t1, t2 -> t1 + t2 }) .map { it.toString() } .subscribe(resultTextView::setText) Up and Down -
Why Reactive Programming? 5. Data streams are composable val upStream
= upButton.clicks().map { 1 } val downStream = downButton.clicks().map { -1 } merge(upStream, downStream) .startWith(0) .scan({ t1, t2 -> t1 + t2 }) .map { it.toString() } .subscribe(resultTextView::setText) Up and Down -
Why Reactive Programming? 5. Data streams are composable val upStream
= upButton.clicks().map { 1 } val downStream = downButton.clicks().map { -1 } merge(upStream, downStream) .startWith(0) .scan({ t1, t2 -> t1 + t2 }) .map { it.toString() } .subscribe(resultTextView::setText) Up and Down -
fun callbackHellExample() { favoriteButton.setOnClickListener(View.OnClickListener { }) } Why Reactive
Programming? 5. Data streams are composable -
fun callbackHellExample() { favoriteButton.setOnClickListener(View.OnClickListener { Api.favoriteCharacter(!character.isFavorite, object : Callback<Boolean> {
override fun success(isFavorite: Boolean) { } override fun error(throwable: Throwable) { showMessage("Error: " + throwable.message) } }) }) } Why Reactive Programming? 5. Data streams are composable -
fun callbackHellExample() { favoriteButton.setOnClickListener(View.OnClickListener { Api.favoriteCharacter(!character.isFavorite, object : Callback<Boolean> {
override fun success(isFavorite: Boolean) { character.save(isFavorite, object : Callback<Character> { }) } override fun error(throwable: Throwable) { showMessage("Error: " + throwable.message) } }) }) } Why Reactive Programming? 5. Data streams are composable -
fun callbackHellExample() { favoriteButton.setOnClickListener(View.OnClickListener { Api.favoriteCharacter(!character.isFavorite, object : Callback<Boolean> {
override fun success(isFavorite: Boolean) { character.save(isFavorite, object : Callback<Character> { override fun success(character: Character) { updateUI(character) } override fun error(otherThrowable: Throwable) { showMessage("Error: " + otherThrowable.message) } }) } override fun error(throwable: Throwable) { showMessage("Error: " + throwable.message) } }) }) } Why Reactive Programming? 5. Data streams are composable -
Why Reactive Programming? 5. Data streams are composable fun reactiveExample()
{ favoriteButton.clicks() } -
Why Reactive Programming? 5. Data streams are composable fun reactiveExample()
{ favoriteButton.clicks() .flatMap { Api.favorite(!character.isFavorite) } } -
Why Reactive Programming? 5. Data streams are composable fun reactiveExample()
{ favoriteButton.clicks() .flatMap { Api.favorite(!character.isFavorite) } .flatMap { isFavorite: Boolean -> character.save(isFavorite) } } -
Why Reactive Programming? 5. Data streams are composable fun reactiveExample()
{ favoriteButton.clicks() .flatMap { Api.favorite(!character.isFavorite) } .flatMap { isFavorite: Boolean -> character.save(isFavorite) } .doOnError { error: Throwable -> showMessage("Error: {error.message}") } } -
Why Reactive Programming? 5. Data streams are composable fun reactiveExample()
{ favoriteButton.clicks() .flatMap { Api.favorite(!character.isFavorite) } .flatMap { isFavorite: Boolean -> character.save(isFavorite) } .doOnError { error: Throwable -> showMessage("Error: {error.message}") } .subscribe { character: Character -> updateUI(character) } } -
Schedulers Schedulers when and where work is performed Why Reactive
Programming? 6. Concurrency -
Why Reactive Programming? 6. Concurrency fun reactiveExample() { favoriteButton.clicks() .flatMap
{ Api.favorite(!character.isFavorite) } .flatMap { isFavorite: Boolean -> character.save(isFavorite) } .doOnError { error: Throwable -> showMessage("Error: {error.message}") } .subscribe { character: Character -> updateUI(character) } } -
Why Reactive Programming? 6. Concurrency fun reactiveExample() { favoriteButton.clicks() .flatMap
{ Api.favorite(!character.isFavorite) } .flatMap { isFavorite: Boolean -> character.save(isFavorite) } .subscribeOn(Schedulers.newThread()) .observeOn(AndroidSchedulers.mainThread()) .doOnError { error: Throwable -> showMessage("Error: {error.message}") } .subscribe { character: Character -> updateUI(character) } } -
Pitfalls • Backpressure • Memory issues • Logging Why Reactive
Programming? 7. Conclusion -
• http://reactivex.io • https://gist.github.com/staltz/868e7e9bc2a7b8c1f754 • http://blog.danlew.net/2017/07/27/an-introduction-to-functional- reactive-programming/ • http://joshaber.github.io/2013/02/11/input-and-output/ •
https://github.com/felipehjcosta/kotlin-rxjava-android • https://github.com/felipehjcosta/reactive-programming-samples Links Why Reactive Programming? 7. Conclusion