Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Design Reactive Apps in Kotlin
Search
VoxxedSG
June 17, 2018
Programming
64
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Design Reactive Apps in Kotlin
VoxxedSG
June 17, 2018
More Decks by VoxxedSG
See All by VoxxedSG
Reactive Frontends with RxJS and Angular
voxxedsgorganizers
0
110
How improvisation boosts creativity and collaboration
voxxedsgorganizers
0
38
How Honestbee does CI/CD on Kubernetes
voxxedsgorganizers
0
87
The Rationale for Relational
voxxedsgorganizers
0
130
Distributed Ledger
voxxedsgorganizers
0
50
Other Decks in Programming
See All in Programming
Long Living Tokens, Die, Die, Die! (or: The One Ring is Banned in Modern)
sylph01
0
320
外から家のPCの電源入れたい!をPHPで解決する
kawashimo0
1
140
UnityでSystem.Net.WebSocketsなWebSocketサーバが動かないのでUnity Monoのコードを覗いてみた / about implementing websocket server with unity mono
drumath2237
1
610
パスワード再設定メールのURLについて知っておこう
uyuki234
0
160
Vue Fes Japan の “作る側”に関わってみて
naokihaba
0
160
Herb in Rails 8.2: Your ERB views, now HTML-aware @ Rails World 2026, Austin, Texas
marcoroth
0
220
Augmenting AI with the Power of Jakarta EE
ivargrimstad
0
200
GemmaをJevのように使ってみる / Use Gemma like Jev
kishida
5
790
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
750
Kotlinの15年とServer-Side Kotlin
n_takehata
1
150
[ハンズオン]AIへの指示だけで「五目並べ」を作ってみよう
satoshi256kbyte
1
380
動作中のプログラムの中身をリアルタイムに覗く / Realtime Debugger for CSharp with Roslyn
prota
1
1.9k
Featured
See All Featured
Mind Mapping
helmedeiros
1
400
What’s in a name? Adding method to the madness
productmarketing
PRO
24
4.2k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
590
Test your architecture with Archunit
thirion
2
2.4k
The untapped power of vector embeddings
frankvandijk
2
1.9k
GraphQLとの向き合い方2022年版
quramy
50
15k
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
1
280
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
62k
RailsConf 2023
tenderlove
30
1.6k
Future Trends and Review - Lecture 12 - Web Technologies (1019888BNR)
signer
PRO
0
3.8k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
7k
brightonSEO & MeasureFest 2025 - Winning Strategies for Black Friday CRO & PPC - Christian Goodrich
cargoodrich
3
870
Transcript
DESIGN REACTIVE APPS IN KOTLIN
GRAB STEPAN GONCHAROV
3 BACKGROUND ▸ 9+ years in Android development ▸ Exploring
Kotlin since 2014 ▸ Organiser of Kotlin User Group Singapore ▸ Using Kotlin in production app since 2016
4 WHAT THIS TALK IS ABOUT ▸ How to design
your app components by example ▸ How Kotlin would help you with that ▸ Discussed solutions could be applied for most of the languages on many platforms
5 PLAN ▸ Kotlin ▸ Reactive Extensions ▸ Problem -
solution ▸ Extending solution to handle more scenarios
6 KOTLIN ▸ Programming language by JetBrains ▸ Officially supported
by Google ▸ Could be complied for Android/JVM/JS/Native platforms ▸ Supports lots of modern programming concepts ▸ Looks a lot like Swift
7 REACTIVE EXTENSIONS ▸ Modern approach to async programming ▸
Extremely popular in Android community ▸ Supports by most of modern programming languages
SOLVE ONCE… USE EVERYWHERE
PROBLEM 1: RUNNING DUPLICATED JOBS
9 SCENARIOS ▸ Save form: Duplicated requests caused by multiple
clicks
10 IS IT SAME JOB? interface Act { val id:
String } ▸ Should have ID
KOTLIN FACTS: INTERFACE COULD HAVE PROPERTIES
12 HOW? ▸ Requirements should be split into small pieces
▸ Implementation should be split into small pieces
13 IS ID ENOUGH? ▸ How we would check is
job with same ID is running? ▸ Who will cancel duplicated jobs? ▸ How to prevent jobs from being launched without checks
14 ABSTRACT EXECUTOR: AGENT ▸ Checks if job is in
progress ▸ Start execution if no jobs with same id is running ▸ Provides error handling callbacks ▸ Cancel current execution
15 ABSTRACT EXECUTOR: AGENT interface Agent { fun execute(executable: Act,
e: (Throwable) -> Unit = ::logError) fun cancel(id: String) fun cancelAll() }
IMPLEMENTATION TIME
17 interface Act { val id: String } class CompletableAct(
override val id: String, override val completable: Completable ) : Act class SingleAct<T : Any>( override val id: String, override val single: Single<T> ) : Act ABSTRACT EXECUTOR: AGENT
class AgentImpl : Agent { val map = ConcurrentHashMap<String, Disposable>()
fun execute(act: Act, e: (Throwable) -> Unit) = when { map.containsKey(act.id) -> log("${act.id} - in progress") else -> startExecution(act, e) .apply { log(“${act.id} - Started”) } } … AGENT IMPLEMENTATION 18
class AgentImpl : Agent { … fun startExecution(act: Act, e:
(Throwable) -> Unit) { val removeFromMap = { map.remove(act.id) } when (act) { is CompletableAct -> act.completable .doFinally(removeFromMap) .subscribe({}, e) is SingleAct<*> -> act.single .doFinally(removeFromMap) .subscribe({}, e) else -> throw IllegalArgumentException() }.let { map.put(act.id, it) } } } AGENT IMPLEMENTATION 19
KOTLIN FACTS: KEEP FUNCTION AS LAST ARGUMENT TO ALLOW SPECIAL
SYNTAX
21 A BIT OF EXTENSIONS fun Completable.toAct(id: String): Act =
CompletableAct(id, this) fun <T: Any> Single<T>.toAct(id: String): Act = SingleAct(id, this)
KOTLIN FACTS: ANY CLASS COULD BE EXTENDED
23 RESULT val a = AgentImpl() a.execute(Completable.timer(2, SECONDS).toAct("Hello")) a.execute(Completable.timer(2, SECONDS).toAct("Hello"))
a.execute(Completable.timer(2, SECONDS).toAct("Hello")) Hello - Act Started Hello - Act Duplicate Hello - Act Duplicate Hello - Act Finished
PROBLEM 2 WHICH JOB WE NEED TO CANCEL?
25 SCENARIOS ▸ Refresh: Cancel second refresh call ▸ User
profile update: Cancel first update
26 STRATEGIES interface StrategyHolder { val strategy: Strategy } sealed
class Strategy object KillMe : Strategy() object SaveMe : Strategy()
KOTLIN FACTS: SEALED CLASSES ARE COOL
28 MODIFY EXISTING COMPONENTS interface Act : StrategyHolder { val
id: String } class CompletableAct( override val id: String, override val completable: Completable, override val strategy: Strategy = SaveMe ) : Act
29 STRATEGIES override fun execute(act: Act, e: (Throwable) -> Unit)
= when { map.containsKey(act.id) -> when (act.strategy) { KillMe -> { cancel(act.id) startExecution(act, e) } SaveMe -> log("${act.id} - Act duplicate") } else -> startExecution(act, e) }
30 RESULT val a = AgentImpl() a.execute(Completable.timer(2, SECONDS) .toAct(“Hello”, KillMe))
a.execute(Completable.timer(2, SECONDS) .toAct(“Hello”, KillMe)) a.execute(Completable.timer(2, SECONDS) .toAct(“Hello”, KillMe)) Hello - Act Started Hello - Act Canceled Hello - Act Started Hello - Act Canceled Hello - Act Started Hello - Act Finished
31 WHAT WE ACHIEVED SO FAR? ▸ Could compare Act’s
▸ Actor could decide if Act’s needs to run based on id and strategy ▸ Subscriptions managed by Agent ▸ Errors handled by Agent
PROBLEM 3 ID IS NOT ENOUGH
33 SCENARIOS ▸ Like/dislike: dislike should cancel like
34 GROUPS AND GROUP STRATEGIES interface GroupStrategyHolder { val groupStrategy:
GroupStrategy val groupKey: String } sealed class GroupStrategy object Default : GroupStrategy() object KillGroup : GroupStrategy()
35 GROUPS AND GROUP STRATEGIES interface Act : StrategyHolder, GroupStrategyHolder
{ val id: String } class CompletableAct( override val id: String, override val completable: Completable, override val strategy: Strategy = SaveMe, override val groupStrategy: GroupStrategy = Default override val groupKey: String = “” ) : Act
36 AGENT IMPLEMENTATION typealias ActKey = String typealias GroupKey =
String typealias GroupMap = ConcurrentHashMap<ActKey, Disposable> … private val groupsMap = ConcurrentHashMap<GroupKey, GroupMap>() override fun execute(act: Act, e: (Throwable) -> Unit) { val actsMap = groupsMap[act.groupKey] ?: ConcurrentHashMap<ActKey, Disposable>() .apply { groupsMap[act.groupKey] = this } if (act.groupStrategy == KillGroup) actsMap.values.forEach { it.dispose() } … }
KOTLIN FACTS: WHEN THINGS GETS MESSY USE TYPE ALIAS
38 RESULT val a = AgentImpl() a.execute(Completable.timer(2, SECONDS).toAct( id =
"Like", groupStrategy = KillGroup, groupKey = "Like-Dislike-PostId-1234")) a.execute(Completable.timer(2, SECONDS).toAct( id = "Dislike", groupStrategy = KillGroup, groupKey = "Like-Dislike-PostId-1234")) a.execute(Completable.timer(2, SECONDS).toAct( id = "Like", groupStrategy = KillGroup, groupKey = "Like-Dislike-PostId-1234")) Like - Act Started Like - Act Canceled Dislike - Act Started Dislike - Act Canceled Like - Act Started Like - Act Finished
KOTLIN FACTS: WHEN THINGS GETS MESSY USE NAMED ARGS
40 PAST VIEW MODEL / PRESENTER START JOBS SUBSCRIPTIONS MANAGEMENT
CODE DUPLICATION EXECUTION STRATEGIES LOGIC JOB GROUPS MANAGEMENT VIEW STATE LIFECYCLE
41 FUTURE VIEW MODEL / PRESENTER AGENT START JOBS SUBSCRIPTIONS
MANAGEMENT LIFECYCLE JOB GROUPS MANAGEMENT ACT’S VIEW STATE EXECUTION STRATEGIES DECLARATIONS
EXTENSIONS
43 SUPERCHARGE YOUR AGENT ▸ Lifecycle ▸ Persistency ▸ Plugins
▸ Metrics
44 LIFECYCLE class AgentImpl(lifecycle: Lifecycle) : Agent { init {
lifecycle.doOnDestroy { cancelAll() } } …
44 PLUGINS FOR AGENTS override fun execute(executable: Act, eh: (Throwable)
-> Unit): Mission { processGroupStrategy(executable) return processActStrategy(executable) }
45 PERSISTENCY ▸ Code inside separate classes ▸ Handle execution
error ▸ Save Act to DB ▸ Restore Act from DB ▸ Act chains
46 METRICS ▸ Realtime monitor for Agent ▸ How many
acts running ▸ Execution time ▸ Errors
THANK YOU!
WE ARE HIRING! @stepango
[email protected]
QUESTIONS Source code: github.com/stepango/akt