Kotlin Multiplatform. Practical multithreading (part 2)

Anna Zharkova
Dec 21, 2020 · 4 min read
Image for post
Image for post

Hi everyone! My name is Anna Zharkova, I’m Lead Mobile developer in “Usetech” software company.

In previous article I demontstrated one of the possible solutions to implement multitheading in Kotlin Multiplatform application. In this part I’m going to describe another solution, it will be KMM application with fully shared common code and common multithreading in shared business logic.

Image for post
Image for post

In previous samplewe used Ktor to make our common network client. This library makes all asynchronous work under its hood. In this case we have no need event to use DispatchQueue in our iOS native application. But in other cases we should use queues to request our business logic correctly and process the result responses. We also used MainScope to call suspended methods in our native Android app.

So if we want to implement multithreading in our shared code with common logic, we should use coroutines for all parts of our project, so we need to setup correct scopes and contexts of our coroutines.

Let’s begin with something simple. First of all, we will create our intemediate architectual component. I will use MVP pattern, so I need to make my presenters. This presenter will call all the methods of the specified service in its own CoroutineScope initialized with the CoroutineContext:

So, as previously mentioned, the presenter requests service methods in specified scope and then deliver the results to our UI:

We need to specify CoroutineDispatcher to initialize our CoroutineScope with CoroutineContext.

We need to use a platform-specific code, that’s why we’re going to customize it with expect/actual mechanism.

uiDispatcher should be used with UI-thread logic and other logic will be requested with defaultDispatcher.

It could be easily done in our androidMain, because it uses Kotlin JVM, so there are default dispatchers for both cases. Dispatchers.Default is a default dispatcher for Coroutines mechanism:

CoroutineDispatcher uses specified fabric MainDispatcherLoader under the hood to create MainCoroutineDispatcher for requesting platform:

Same mechanism used for DefaultDispatcher:

But not for all native platforms we can use existed default coroutine dispatchers. For example, such platforms as iOS work with KMM via Kotlin/Native, not Kotlin/JVM.

So if we try to use the same implementation, as we used for Android, we will receive an error:

Image for post
Image for post

Let’s take a look, what has happened.

GitHub Kotlin Coroutines Issue 470 contains information, that these special dispatchers for iOS haven’t been created in Kotlin/Native yet:

Image for post
Image for post

Issue 470 depends on Issue 462, so it is also not resolved:

Image for post
Image for post

Recommended solution for our case is to create our own dispatchers:

We have created MainDispatch with DispatchQueue.main and IODispatcher with DispatchQueue.global(). When we launch our code, we will get the same error.

The problem is, we cannot use dispatch_get_global_queue to dispatch our coroutines, because it is not bound to any particular thread in Kotlin/Native:

Image for post
Image for post

Secondly, Kotlin/Native doesn’t allow to move any mutable objects between threads. Included the coroutines.

So we can try to use MainDispatcher for all our cases:

But it is not enough. We also should freeze our objects before sharing the between threads. So we need to use freeze() command in this case:

Image for post
Image for post

But if we try to perform freeze() on already frozen object, FreezingException will be thrown. For example, all singletones are frozen by default.

That’s why we should use @ThreadLocal annotation to share singletones and @SharedImmutable for global variables:

We can simply use MainDispatcher for all our needs, when we use Ktor or another library that supports its own asynchronous processing. In common case for all long-running work we should use GlobalScope with the context of Dispatchers.Main/MainDispatcher:

So we can easily perform the switching between contexts to our service logic:

We created the common scope for all code performing in same suspended function. So everything will work correctly. It is not the only way to implement coroutine based logic, you can organize it with any approach you prefer.

You can also use wrapping blocks to share code with DispatchQueue.global():

Of course, you need to implement actual fun callFreeze(…) in androidMain, but you need just to put a callback into the completion block.

Finally, we got a completed application that works same way in both platforms:

Image for post
Image for post

Sample code
One more sample:
github.com/anioutkazharkova/kmp_news_sample

tproger.ru/articles/creating-an-app-for-kotlin-multiplatform
github.com/JetBrains/kotlin-native
github.com/JetBrains/kotlin-native/blob/master/IMMUTABILITY.md
github.com/Kotlin/kotlinx.coroutines/issues/462
helw.net/2020/04/16/multithreading-in-kotlin-multiplatform-apps

Originally published at https://habr.com.

Pretty geek notes

Mobile development articles and news

Anna Zharkova

Written by

My name is Anna Zharkova, I'm from Barnaul.I'm Lead Mobile developer with more than 7 years of experience. I develop both native and cross platform application

Pretty geek notes

Mobile development articles and news

Anna Zharkova

Written by

My name is Anna Zharkova, I'm from Barnaul.I'm Lead Mobile developer with more than 7 years of experience. I develop both native and cross platform application

Pretty geek notes

Mobile development articles and news

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store