Slide 1

Slide 1 text

Kotlin Multiplatform. Advanced multithreading Anna Zharkova Lead Mobile developer

Slide 2

Slide 2 text

Introduction • Mobile developer since 2013 • Lead Mobile developer in «Usetech software» . • Native mobile development, iOS and Android (Swift/Objective-C, Kotlin/Java) cross-platform mobile development (Xamarin, Kotlin multiplatform) • Leading mobile team. Mentorship • Public speaking (Mobius Moscow 2020, AppsLive 2020) • Tutor in Otus (iOS advanced) • Writing articles about mobile development (especially iOS and KMM ) • Women Techmaker Ambassador since 2021 2

Slide 3

Slide 3 text

Goals. Today discuss: • Kotlin Multiplatform and common concurrency • Coroutines, solving problems in Kotlin/Native • Concurrency without coroutines. Background Workers • Advanced Sharing • Flows & Kotlin/Native, iOS side 3

Slide 4

Slide 4 text

Kotlin Multiplatform 4

Slide 5

Slide 5 text

Kotlin Multiplatform 5 • SDK, not framework • JVM for Android and LLVM for iOS • Total sharing (almost)

Slide 6

Slide 6 text

Every platform needs its Kotlin 6 Kotlin Common Kotlin JVM Kotlin JS Kotlin Native

Slide 7

Slide 7 text

Concurrency in Kotlin/Native 7 expect Dispatchers iOS: actual Dispatchers Android: actual Dispatchers Kotlin Native Kotlin JVM Kotlin JVM concurrency != Kotlin Native concurrency

Slide 8

Slide 8 text

Networking as a case Common implementation (e.g. use Ktor) -> Coroutines Separated native -> Coroutines, native concurrency 8 Depends on app architecture

Slide 9

Slide 9 text

Sample. Networking with Ktor and Coroutines 9 Common concurrency with Coroutines Ktor implements async work under its hood

Slide 10

Slide 10 text

Coroutines vs GCD. Suspended 10 Suspended not blocking in iOS Suspended Kotlin easy transforms into Swift (since Kotlin Native 1.4)

Slide 11

Slide 11 text

Problem of common architecture 11 Network service API service Common architecture iOS View Android View Common Work with coroutines Common architecture = common scope

Slide 12

Slide 12 text

Setting up thread and contexts 12 Android/ iOS : Dispatchers.Default (Android) Dispatchers.Main – unique for platform

Slide 13

Slide 13 text

Dispatchers.Main inside 13 Dispatchers factory in MainCoroutineDispatcher MainDispatchers.kt

Slide 14

Slide 14 text

Let’s focus in Kotlin/Native Kotlin/JVM is OK 14

Slide 15

Slide 15 text

Setting up thread and contexts 15 iOS: using Default dispatcher Something went wrong

Slide 16

Slide 16 text

Missed Dispatchers in iOS 16 No default dispatcher for iOS https://github.com/Kotlin/kotlinx.coroutines/issues/470

Slide 17

Slide 17 text

Missed Dispatchers in iOS 17 Issue 462 still be not solved https://github.com/Kotlin/kotlinx.coroutines/issues/462

Slide 18

Slide 18 text

CoroutineDispatcher for iOS 18 iOS: own CoroutineDispatcher to be used as a context

Slide 19

Slide 19 text

CoroutineDispatcher for iOS 19 Not available to create working dispatcher bound to DispatchersQueue background We have to use Main as default (Dispatchers.Default not working)

Slide 20

Slide 20 text

CoroutineDispatcher for iOS 20 Use MainDispatcher block.run().freeze()???

Slide 21

Slide 21 text

How to work with threads in Kotlin/Native 21 • Mutable == for one thread • Immutable == multiple thread In Kotlin JVM everything is already done

Slide 22

Slide 22 text

How to work with threads in Kotlin/Native 22 Object should be freezed to become immutable https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.native.concur rent/freeze.html

Slide 23

Slide 23 text

How to work with threads in Kotlin/Native 23 Background could be called like this

Slide 24

Slide 24 text

Why not to use GlobalScope 24 Default background work? Not recommended Potential leaks and losing of callbacks ^ l Fix

Slide 25

Slide 25 text

Immutable problems 25 Singletones and constants are frozen by default

Slide 26

Slide 26 text

Immutable problems 26 Use @ThreadLocal for objects

Slide 27

Slide 27 text

Immutable problems 27 Use @ThreadLocal for objects

Slide 28

Slide 28 text

It can be not only Coroutines 28

Slide 29

Slide 29 text

Different synchronization 29 Collections, thread-safe read write

Slide 30

Slide 30 text

Deal without coroutines 30 Sample: Networking without Ktor and Coroutines

Slide 31

Slide 31 text

Deal with workers 31 iOS Http Client with Kotlin

Slide 32

Slide 32 text

Deal with workers 32 iOS Http Client with Kotlin ßBackground <- Main

Slide 33

Slide 33 text

Worker. Background 33 Do background with Worker Don’t forget to share (freeze)!

Slide 34

Slide 34 text

Worker. Background 34 Apply to code What about return to main?

Slide 35

Slide 35 text

Worker. Background 35 Worker with return Problem: Same thread. How to switch to main??? <- return

Slide 36

Slide 36 text

Rout to Main 36 Dispatch_queue_main + freeze() Don’t forget to apply main{} to code! <- return

Slide 37

Slide 37 text

Sharing data and variables 37 Everything to share should be freezed InvalidMutabilityException: mutation attempt of frozen

Slide 38

Slide 38 text

Still crashes We’ve forgot to deal with mutables 38

Slide 39

Slide 39 text

Sharing data and variables 39 Crash!!!! InvalidMutabilityException: mutation attempt of frozen

Slide 40

Slide 40 text

Sharing data and variables 40 @ThreadLocal – objects and backing fields only!

Slide 41

Slide 41 text

Sharing data and variables 41 ThreadLocal without annotation (attempt) Inspired by https://github.com/ktorio/ktor

Slide 42

Slide 42 text

Sharing data and variables 42 Freeze exception Not working without freeze

Slide 43

Slide 43 text

Sharing data and variables. Atomic 43 AtomicReferece wrapper Potential leaks

Slide 44

Slide 44 text

Sharing data and variables.Atomic 44 AtomicReferece wrapper Fix leaks

Slide 45

Slide 45 text

Sharing data and variables 45 DetachedObjectGraph Attach only once!

Slide 46

Slide 46 text

If it’s too complicated, let’s return to coroutines 46

Slide 47

Slide 47 text

Other ways to communicate between contexts 47 kotlinx.coroutines • Channels • Flows • CompletableDeffered

Slide 48

Slide 48 text

Other ways to communicate between contexts 48 kotlinx.coroutines • Channels • Flows • CompletableDeffered

Slide 49

Slide 49 text

Other ways to communicate between contexts 49 kotlinx.coroutines • Channels • Flows • CompletableDeffered

Slide 50

Slide 50 text

Other ways to communicate between contexts 50 kotlinx.coroutines • CompletableDeffered Also solves problem with lambdas sharing

Slide 51

Slide 51 text

Flow & iOS 51

Slide 52

Slide 52 text

Flow in Kotlin Common 52 Asynchronous flow instead of LiveData (LiveData is Android only!)

Slide 53

Slide 53 text

Flow in Kotlin Common 53 Looks strange in iOS side

Slide 54

Slide 54 text

Flow in Kotlin Common 54 Need to use own collector Sometimes signals are missed

Slide 55

Slide 55 text

Flow in Kotlin Common 55 Use custom flow wrapper

Slide 56

Slide 56 text

Flow in Kotlin Common 56 Should be called in coroutines scope Even remove of flowOn doesn’t solve problem

Slide 57

Slide 57 text

Flow in Kotlin Common 57 Flows should be used directly in common part Or we need to make a wrapper on iOS side

Slide 58

Slide 58 text

Flow in Kotlin Common 58 Flows -> Publishers https://gist.github.com/Nillerr/dc437c0248 5da661b4f285cee01069a1

Slide 59

Slide 59 text

Summary. 59 • It hard to work with concurrency in KMM, but you could work with id. • KMM still be improved • Kotlin/Native still be improved –> wait for issue 462 to be solved

Slide 60

Slide 60 text

Sources 60 • https://betterprogramming.pub/using-kotlin-flow-in-swift-3e7b53f559b6 • https://github.com/JetBrains/kotlin-native • https://github.com/JetBrains/kotlin-native/blob/master/IMMUTABILITY.md • https://github.com/Kotlin/kotlinx.coroutines/issues/462 • https://github.com/anioutkazharkova/kmm-concurrency-helper • https://kmpdocs.suparnatural.com/concurrency/#worker • https://elizarov.medium.com/the-reason-to-avoid-globalscope-835337445abc

Slide 61

Slide 61 text

Thanks for listening! 61 @anioutkajarkova azharkova prettygeeknotes