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
設計次第でAIコードの読む量は減らせる / designing-for-code-reading
minodriven
35
17k
Verilogで学ぶCPU自作入門
uyuki234
8
4.1k
phperだってパンチカードでコーディングしたい
o0h
PRO
0
340
個人開発者よ、Honoを使え(おじさんver)
tanahiro2010
2
260
月間100万人のサービス刷新で、 技術選定をHonoに振り切った話
yutobara
8
6.6k
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
1k
なぜCTOを降りてFDEを選んだのか?〜なぜプロダクト企業がFDEで顧客の現場に踏み込むのか〜
gonta
1
210
IBM Bob でAI駆動開発『QC-Playbook』
ichirokano2
0
110
FactoryBotをやめればRSpecは速くなるのか?
yuta_onishi_97
0
520
現場と育てるRailsモデリング 〜Spreadsheetから始めたモデル設計〜
siroemk
2
600
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
390
自サークルのIdPからログアウトできるようになるまで
taiseiue
1
140
Featured
See All Featured
Money Talks: Using Revenue to Get Sh*t Done
nikkihalliwell
0
520
Bioeconomy Workshop: Dr. Julius Ecuru, Opportunities for a Bioeconomy in West Africa
akademiya2063
PRO
1
410
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.9k
BBQ
matthewcrist
90
10k
Automating Front-end Workflow
addyosmani
1369
210k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
280
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
2
2k
A Modern Web Designer's Workflow
chriscoyier
699
190k
Testing 201, or: Great Expectations
jmmastey
46
8.3k
The Curious Case for Waylosing
cassininazir
1
570
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
3.1k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
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