[Разработка мобильных приложений] Kotlin Multiplatform. Работаем с многопоточностью на практике. Ч.1
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Доброго всем времени суток! С вами я, Анна Жаркова, ведущий мобильный разработчик компании «Usetech»
Я давно занимаюсь не только нативной разработкой (как iOS, так и Android), но и кросс-платформенной. В свое время я очень плотно писала на Xamarin (iOS, Android, так и Forms). Так как я интересуюсь различными технологиями шаринга кода, то не прошла и мимо Kotlin Multiplatform (KMM). И сегодня мы с вами поговорим об этом SDK, и как с ним работать на практике.
В сети хватает базовых примеров приложений на KMM, поэтому мы рассмотрим что-то, более приближенное к нашим ежедневным разработческим задачам, а именно, как реализовать многопоточное приложение на Kotlin Multiplatform.
Для начала немного вводной информации. Если вы уже знакомы с Kotlin Multiplatform, то листайте ниже до примера.
Основная идея KMM, как и других кросс-платформенных технологий — оптимизация разработки путем написания кода один раз и последующего его использования на разных платформах.
Согласно концепции JetBrains, Kotlin Multiplatform не является фреймворком. Это именно SDK, который позволяет создавать модули с общим кодом, подключаемые к нативным приложениям.
Для взаимодействия с платформами используются специфические для платформы версии Kotlin: Kotlin/JVM, Kotlin/JS, Kotlin/Native. Данные версии включают расширения языка Kotlin, а также специфичные для конкретной платформы библиотеки и инструменты. Написанный на Kotlin модуль компилируется в JVM байткод для Android и LLVM байткод для iOS.
Модуль (Shared, Common) содержит переиспользуемую бизнес-логику. Платформенные модули iOS/Android, к которым подключен Shared/Common, либо используют написанную логику напрямую, либо имплементируют свою реализацию в зависимости от особенностей платформы.
Общая бизнес-логика может включать в себя:
- сервисы для работы с сетью;
- сервисы для работы с БД;
- модели данных.
Также в нее могут входить архитектурные компоненты приложения, напрямую не включающие UI, но с ним взаимодействующие:
- ViewModel;
- Presenter;
- Интеракторы и т.п.
Концепцию Kotlin Multiplatform можно сравнить с реализацией Xamarin Native. Однако, здесь нет модулей или функционала, реализующих UI. Эта логическая нагрузка ложится на подключенные нативные проекты.
Теперь рассмотрим подход на практике.
Если вы еще не работали с KMM, то потребуется установить и настроить инструменты. Раньше это было довольно хлопотно, но сейчас достаточно установить Android Studio (версии от 4.1) и плагин Kotlin Multiplatform Mobile. Выбираем шаблон KMM Application при создании проекта, и все отработает автоматически.
Мультиплатформенные проекты Kotlin обычно делятся на несколько модулей:
- модуль переиспользуемой бизнес-логики (Shared, commonMain и т.п);
- модуль для IOS приложения (iOSMain, iOSTest);
- модуль для Android приложения (androidMain, androidTest).
В них располагается наша бизнес-логика. Всю используемую в проекте бизнес-логику можно разделить на:
- переиспользуемую (общую);
- платформенную реализацию.
Переиспользуемая логика располагается в проекте commonMain в каталоге kotlin и разделяется на package. Декларации функций, классов и объектов, обязательных к переопределению, помечаются модификатором expect:
Реализации должны иметь модификатор actual.
В качестве примера работы с многопоточностью рассмотрим небольшое приложение, обращающееся к стороннему API по сети:
Я выбрала www.themoviedb.org. Полный код примера будет по ссылке внизу статьи.
В общей Common части расположим общую бизнес-логику:
А именно наш сетевой сервис. Это логично.
В модулях iOS/Android приложений оставим только UI компоненты для отображения списка и адаптеры. iOS часть будет написана на Swift, Android – на Kotlin.
Начнем с бизнес-логики. Т.к весь функционал будет в модуле common, то мы будем использовать в качестве библиотек решения для Kotlin Multiplatform:
Ktor – библиотека для работы с сетью и сериализации.
В build.gradle (:app) пропишем следующие зависимости:
val ktorVersion = "1.4.0"
val serializationVersion = "1.0.0-RC"
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$serializationVersion")
implementation("io.ktor:ktor-client-serialization:$ktorVersion")
}
}
val androidMain by getting {
dependencies {
//...
implementation("io.ktor:ktor-client-android:$ktorVersion")
}
}
val iosMain by getting {
dependencies {
implementation("io.ktor:ktor-client-ios:$ktorVersion")
}
}
...
Также добавим поддержку сериализации:
plugins {
//...
kotlin("plugin.serialization") version "1.4.10"
}
Далее нам надо определить, что делать с многопоточностью, ведь она реализуется по-разному на каждой платформе. На стороне iOS мы используем GCD (Grand Central Dispatch), а на стороне Android — JVM Threads и Coroutines. Однако, в Kotlin Multiplatform мы можем сделать общей и работу с многопоточностью.
Для этого мы будет использовать Kotlin Coroutines:
val coroutinesVersion = "1.3.9-native-mt"
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")
//...
}
}
val androidMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion")
//...
}
}
val iosMain by getting {
dependencies {
//...
}
}
...
Тут стоит сделать пояснение, как с этим работать, потому что далеко не все iOS разработчики знают, что такое Coroutines. Если вкратце, то это блок кода, который можно приостановить, не блокируя поток. У корутины может быть контекст выполнения (CoroutineContext), цикл жизни корутины управляется Job. У корутины есть область действия (CoroutineScope), а поток, в котором она исполняется, задается с помощью CoroutineDispatcher.
Если проводить аналогию с iOS, то это похоже на выполнение блока кода в DispatchQueue, имеющей определенный QoS и привязку к определенному потоку NSThread, либо Operation в OperationQueue, где GlobalScope аналогичен DispatchQueue.global(), а MainScope — DispatchQueue.main:
//Android
fun loadMovies() {
GlobalScope.async {
service.makeRequest()
withContext(uiDispatcher) {
//...
}
}
}
//iOS
func loadMovies() {
DispatchQueue.global().async {
service.makeRequest()
DispatchQueue.main.async{
//...
}
}
}
Еще одной ключевой особенностью корутин является использование слова suspend. Данный модификатор не превращает метод в асинхронный сам по себе, это зависит от других деталей реализации, но маркирует, что его можно приостановить без блокировки потока. Также такой метод можно вызывать только в контексте корутины.
Ktor использует механизм корутины для реализации асинхронной работы, поэтому вызов HttpClient делаем в suspended функции:
//Network service
class NetworkService {
val httpClient = HttpClient {
install(JsonFeature) {
val json = kotlinx.serialization.json.Json { ignoreUnknownKeys = true }
serializer = KotlinxSerializer(json)
}
}
suspend inline fun <reified T> loadData(url: String): T? {
return httpClient.get(url)
}
}
//Movies service
suspend fun loadMovies():MoviesList? {
val url = MY_URL
return networkService.loadData<MoviesList>(url)
}
При подключении Kotlin Coroutines мы не указали никакую особую версию для iOS. Это не ошибка. Дело в том, что начиная с версии Kotlin 1.4 Suspended функция Kotlin легко трансформируется в функцию Swift c completion handler блоком:
func getMovies() {
self.networkService?.loadMovies {(movies, error) in
//...
}
}
Т.к Ktor уже обеспечивает асинхронность, то в данном случае потребности в использовании DispatchQueue на стороне iOS нет.
На стороне Android используем механизм корутинов, и вызов будет иметь вид:
fun getMovies() {
mainScope.launch {
val movies = this.networkService?.loadMovies()
//...
}
}
}
Такой способ обращения к общей логике мы можем использовать при подходе, когда у нас нет общего архитектурного элемента наших нативных приложений, и в Common проекте мы реализуем только бизнес-логику. Это вполне рабочий подход.
Если же мы хотим сделать максимально расшариваемый между наивными проектами общий код, включить туда архитектурное решение, а взаимодействие с UI через протоколы, то нам потребуется поменять работу и с потоками.
Посмотрим это вследующей части
Исходники примера github.com/anioutkazharkova/movies_kmp
Подробнее о работе корутин вы можете узнатьтут
===========
Источник:
habr.com
===========
Похожие новости:
- [Тестирование IT-систем, Разработка мобильных приложений, IT-инфраструктура, Разработка под Android, DevOps] VirtualBox — Запуск Android эмулятора в виртуальной среде для тестирования Android проекта
- [Разработка под iOS] Как мы делаем App Clips?
- [Программирование, Разработка под Android] Разрушаем мифы о производительности Android (перевод)
- [Open source, Разработка мобильных приложений, Flutter] Состояние Flutter на изолятах
- [Программирование, Разработка под iOS, Разработка мобильных приложений, Разработка под Android] Кошелёк Mobile Challenge: итоги конкурса и подробный разбор решений командой разработки
- [Браузеры] Браузер Vivaldi 2020 — Итоги года
- [Информационная безопасность, Разработка мобильных приложений] Пользователи системы «Помощник Москвы» смогут сообщать о незаконной торговле и скоплениях людей
- [Программирование, Разработка мобильных приложений, Dart, Flutter] Flutter под капотом: Owners
- [Работа с видео, Разработка под Android, Компьютерное железо, Видеотехника] Запуск Netflix на телевизорах и приставках. Лишние 40 миллисекунд (перевод)
- [Разработка под iOS, Swift, Тестирование мобильных приложений] Погружение в автотестирование на iOS. Часть 1. Как работать с accessibilityidentifier объектов
Теги для поиска: #_razrabotka_mobilnyh_prilozhenij (Разработка мобильных приложений), #_kotlin_multiplatform, #_ios, #_mnogopotochnost (многопоточность), #_kmm, #_android, #_razrabotka_mobilnyh_prilozhenij (
Разработка мобильных приложений
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 13:56
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Доброго всем времени суток! С вами я, Анна Жаркова, ведущий мобильный разработчик компании «Usetech» Я давно занимаюсь не только нативной разработкой (как iOS, так и Android), но и кросс-платформенной. В свое время я очень плотно писала на Xamarin (iOS, Android, так и Forms). Так как я интересуюсь различными технологиями шаринга кода, то не прошла и мимо Kotlin Multiplatform (KMM). И сегодня мы с вами поговорим об этом SDK, и как с ним работать на практике. В сети хватает базовых примеров приложений на KMM, поэтому мы рассмотрим что-то, более приближенное к нашим ежедневным разработческим задачам, а именно, как реализовать многопоточное приложение на Kotlin Multiplatform. Для начала немного вводной информации. Если вы уже знакомы с Kotlin Multiplatform, то листайте ниже до примера. Основная идея KMM, как и других кросс-платформенных технологий — оптимизация разработки путем написания кода один раз и последующего его использования на разных платформах. Согласно концепции JetBrains, Kotlin Multiplatform не является фреймворком. Это именно SDK, который позволяет создавать модули с общим кодом, подключаемые к нативным приложениям. Для взаимодействия с платформами используются специфические для платформы версии Kotlin: Kotlin/JVM, Kotlin/JS, Kotlin/Native. Данные версии включают расширения языка Kotlin, а также специфичные для конкретной платформы библиотеки и инструменты. Написанный на Kotlin модуль компилируется в JVM байткод для Android и LLVM байткод для iOS. Модуль (Shared, Common) содержит переиспользуемую бизнес-логику. Платформенные модули iOS/Android, к которым подключен Shared/Common, либо используют написанную логику напрямую, либо имплементируют свою реализацию в зависимости от особенностей платформы. Общая бизнес-логика может включать в себя:
Также в нее могут входить архитектурные компоненты приложения, напрямую не включающие UI, но с ним взаимодействующие:
Концепцию Kotlin Multiplatform можно сравнить с реализацией Xamarin Native. Однако, здесь нет модулей или функционала, реализующих UI. Эта логическая нагрузка ложится на подключенные нативные проекты. Теперь рассмотрим подход на практике. Если вы еще не работали с KMM, то потребуется установить и настроить инструменты. Раньше это было довольно хлопотно, но сейчас достаточно установить Android Studio (версии от 4.1) и плагин Kotlin Multiplatform Mobile. Выбираем шаблон KMM Application при создании проекта, и все отработает автоматически. Мультиплатформенные проекты Kotlin обычно делятся на несколько модулей:
В них располагается наша бизнес-логика. Всю используемую в проекте бизнес-логику можно разделить на:
Переиспользуемая логика располагается в проекте commonMain в каталоге kotlin и разделяется на package. Декларации функций, классов и объектов, обязательных к переопределению, помечаются модификатором expect: Реализации должны иметь модификатор actual. В качестве примера работы с многопоточностью рассмотрим небольшое приложение, обращающееся к стороннему API по сети: Я выбрала www.themoviedb.org. Полный код примера будет по ссылке внизу статьи. В общей Common части расположим общую бизнес-логику: А именно наш сетевой сервис. Это логично. В модулях iOS/Android приложений оставим только UI компоненты для отображения списка и адаптеры. iOS часть будет написана на Swift, Android – на Kotlin. Начнем с бизнес-логики. Т.к весь функционал будет в модуле common, то мы будем использовать в качестве библиотек решения для Kotlin Multiplatform: Ktor – библиотека для работы с сетью и сериализации. В build.gradle (:app) пропишем следующие зависимости: val ktorVersion = "1.4.0"
val serializationVersion = "1.0.0-RC" sourceSets { val commonMain by getting { dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion") implementation("io.ktor:ktor-client-core:$ktorVersion") implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$serializationVersion") implementation("io.ktor:ktor-client-serialization:$ktorVersion") } } val androidMain by getting { dependencies { //... implementation("io.ktor:ktor-client-android:$ktorVersion") } } val iosMain by getting { dependencies { implementation("io.ktor:ktor-client-ios:$ktorVersion") } } ... Также добавим поддержку сериализации: plugins {
//... kotlin("plugin.serialization") version "1.4.10" } Далее нам надо определить, что делать с многопоточностью, ведь она реализуется по-разному на каждой платформе. На стороне iOS мы используем GCD (Grand Central Dispatch), а на стороне Android — JVM Threads и Coroutines. Однако, в Kotlin Multiplatform мы можем сделать общей и работу с многопоточностью. Для этого мы будет использовать Kotlin Coroutines: val coroutinesVersion = "1.3.9-native-mt"
sourceSets { val commonMain by getting { dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion") //... } } val androidMain by getting { dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion") //... } } val iosMain by getting { dependencies { //... } } ... Тут стоит сделать пояснение, как с этим работать, потому что далеко не все iOS разработчики знают, что такое Coroutines. Если вкратце, то это блок кода, который можно приостановить, не блокируя поток. У корутины может быть контекст выполнения (CoroutineContext), цикл жизни корутины управляется Job. У корутины есть область действия (CoroutineScope), а поток, в котором она исполняется, задается с помощью CoroutineDispatcher. Если проводить аналогию с iOS, то это похоже на выполнение блока кода в DispatchQueue, имеющей определенный QoS и привязку к определенному потоку NSThread, либо Operation в OperationQueue, где GlobalScope аналогичен DispatchQueue.global(), а MainScope — DispatchQueue.main: //Android
fun loadMovies() { GlobalScope.async { service.makeRequest() withContext(uiDispatcher) { //... } } } //iOS
func loadMovies() { DispatchQueue.global().async { service.makeRequest() DispatchQueue.main.async{ //... } } } Еще одной ключевой особенностью корутин является использование слова suspend. Данный модификатор не превращает метод в асинхронный сам по себе, это зависит от других деталей реализации, но маркирует, что его можно приостановить без блокировки потока. Также такой метод можно вызывать только в контексте корутины. Ktor использует механизм корутины для реализации асинхронной работы, поэтому вызов HttpClient делаем в suspended функции: //Network service
class NetworkService { val httpClient = HttpClient { install(JsonFeature) { val json = kotlinx.serialization.json.Json { ignoreUnknownKeys = true } serializer = KotlinxSerializer(json) } } suspend inline fun <reified T> loadData(url: String): T? { return httpClient.get(url) } } //Movies service suspend fun loadMovies():MoviesList? { val url = MY_URL return networkService.loadData<MoviesList>(url) } При подключении Kotlin Coroutines мы не указали никакую особую версию для iOS. Это не ошибка. Дело в том, что начиная с версии Kotlin 1.4 Suspended функция Kotlin легко трансформируется в функцию Swift c completion handler блоком: func getMovies() {
self.networkService?.loadMovies {(movies, error) in //... } } Т.к Ktor уже обеспечивает асинхронность, то в данном случае потребности в использовании DispatchQueue на стороне iOS нет. На стороне Android используем механизм корутинов, и вызов будет иметь вид: fun getMovies() {
mainScope.launch { val movies = this.networkService?.loadMovies() //... } } } Такой способ обращения к общей логике мы можем использовать при подходе, когда у нас нет общего архитектурного элемента наших нативных приложений, и в Common проекте мы реализуем только бизнес-логику. Это вполне рабочий подход. Если же мы хотим сделать максимально расшариваемый между наивными проектами общий код, включить туда архитектурное решение, а взаимодействие с UI через протоколы, то нам потребуется поменять работу и с потоками. Посмотрим это вследующей части Исходники примера github.com/anioutkazharkova/movies_kmp Подробнее о работе корутин вы можете узнатьтут =========== Источник: habr.com =========== Похожие новости:
Разработка мобильных приложений ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 13:56
Часовой пояс: UTC + 5