Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Design Reactive Apps in Kotlin
Search
VoxxedSG
June 17, 2018
Programming
62
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
36
How Honestbee does CI/CD on Kubernetes
voxxedsgorganizers
0
85
The Rationale for Relational
voxxedsgorganizers
0
130
Distributed Ledger
voxxedsgorganizers
0
47
Other Decks in Programming
See All in Programming
kubernetes コンポーネント開発入門 / 新卒N年目の勉強会&交流会!〜〇〇への誘い〜 #n_study
mazrean
0
160
XHTMLが残したもの
yosuke_furukawa
PRO
1
380
{ Android | Kotlin } Gradle Plugin in 2026
ryunen344
1
270
AI に Inclusive UI を書かせよう — Design Rules Skill で Compose UI を作り直す
theoriatec2024
1
430
DroidKaigi 2026 「個人開発という実験場: Android エンジニアが手にする4つの自由」
slashnephy
0
200
[PyCon KR 2026] More Variants, More Diversity for AI Accelerators
achimnol
0
130
AIと壁打ちしながら進めるコスト管理
fufuhu
2
1.9k
KotlinConf Extended South Korea 2026 Keynote
l2hyunwoo
0
130
30年振りにコンパイラの定数整数除算を改善した
herumi
9
4.4k
Deep dive into the select statement (GopherCon UK)
jespino
0
170
レビュー履歴をAIに食わせて、 Compose移行を加速するs
shihochan
0
260
go-spidermonkeyでAIエージェントのCode Modeを実装する
syumai
3
1.5k
Featured
See All Featured
Paper Plane
katiecoart
PRO
2
53k
Building Flexible Design Systems
yeseniaperezcruz
330
41k
The Curious Case for Waylosing
cassininazir
1
490
GraphQLとの向き合い方2022年版
quramy
50
15k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
490
Deep Space Network (abreviated)
tonyrice
0
290
Building the Perfect Custom Keyboard
takai
2
860
The Spectacular Lies of Maps
axbom
PRO
1
970
The untapped power of vector embeddings
frankvandijk
2
1.9k
Between Models and Reality
mayunak
4
450
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
230
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
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