Что такое Kotlin Multiplatform и зачем он нужен?
Kotlin Multiplatform (KMP) — это не библиотека в классическом понимании, а технология (SDK и набор инструментов) от JetBrains, которая позволяет писать общий код на языке Kotlin и компилировать его для нескольких платформ: JVM (Android, серверы), JavaScript (веб-браузеры, Node.js), Native (iOS, macOS, Windows, Linux, watchOS, tvOS).
Зачем это нужно? В современной разработке часто требуется создавать приложения для разных платформ. Традиционно это означало написание кода на разных языках (Swift для iOS, Java/Kotlin для Android, JavaScript для веба). Kotlin Multiplatform решает эту проблему, позволяя вынести бизнес-логику (сетевые запросы, работа с базой данных, валидация данных, сериализация) в общий модуль, который используется на всех платформах. Платформенно-зависимый код (UI, работа с камерой, сенсорами) остается нативным, но его объем значительно сокращается.
Ключевые преимущества:
- Сокращение времени разработки: Один раз пишете логику — используете везде.
- Единая кодовая база: Меньше багов, проще поддерживать и рефакторить.
- Нативная производительность: В отличие от React Native или Flutter, KMP компилируется в нативный код, а не работает через прослойку.
- Гибкость: Вы можете начать с малого — вынести только одну библиотеку (например, для сетевых запросов) и постепенно расширять общий код.
- Использование существующего кода: Можно подключать нативные библиотеки через механизм
expect/actual.
Kotlin Multiplatform идеально подходит для: мобильных приложений (Android + iOS), серверных приложений (Ktor + KMP), веб-приложений (Kotlin/JS), десктопных приложений (Compose Multiplatform) и библиотек, которые должны работать везде.
Установка и настройка
Kotlin Multiplatform не устанавливается через pip или npm. Это инструмент сборки, который настраивается через систему сборки Gradle. Ниже приведен стандартный процесс для создания проекта с KMP.
1. Установка IntelliJ IDEA или Android Studio
Рекомендуется использовать IntelliJ IDEA Ultimate (Community Edition тоже подойдет, но с ограничениями) или Android Studio с плагином Kotlin Multiplatform.
2. Создание проекта
Самый простой способ — использовать Kotlin Multiplatform Wizard (онлайн-инструмент от JetBrains): https://kmp.jetbrains.com/. Выберите нужные платформы (Android, iOS, Desktop, Web) и скачайте готовый шаблон.
3. Структура проекта (Gradle)
Основной файл конфигурации — build.gradle.kts (корневой и модульные). Ключевые плагины:
// Корневой build.gradle.kts
plugins {
kotlin("multiplatform") version "1.9.22" apply false
kotlin("plugin.serialization") version "1.9.22" apply false
id("com.android.application") version "8.2.0" apply false
id("org.jetbrains.compose") version "1.5.11" apply false
}
// Модуль shared (общий код)
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
id("com.android.library")
}
kotlin {
androidTarget {
compilations.all {
kotlinOptions {
jvmTarget = "1.8"
}
}
}
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "shared"
isStatic = true
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
implementation("io.ktor:ktor-client-core:2.3.7")
implementation("io.ktor:ktor-client-content-negotiation:2.3.7")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
}
}
val androidMain by getting {
dependencies {
implementation("io.ktor:ktor-client-android:2.3.7")
}
}
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
dependencies {
implementation("io.ktor:ktor-client-darwin:2.3.7")
}
}
}
}
android {
namespace = "com.example.shared"
compileSdk = 34
defaultConfig {
minSdk = 24
}
}
Быстрый старт с примером кода
Создадим простой класс для работы с пользователями в общем модуле (commonMain).
// commonMain/kotlin/com/example/shared/UserRepository.kt
package com.example.shared
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.coroutines.delay
@Serializable
data class User(
val id: Int,
val name: String,
val email: String
)
class UserRepository {
private val json = Json { ignoreUnknownKeys = true }
// Симуляция загрузки пользователя из сети
suspend fun fetchUser(userId: Int): User {
delay(1000) // Имитация задержки сети
return User(
id = userId,
name = "User_$userId",
email = "user$userId@example.com"
)
}
// Парсинг JSON в объект User
fun parseUser(jsonString: String): User {
return json.decodeFromString(jsonString)
}
// Сериализация объекта User в JSON
fun serializeUser(user: User): String {
return json.encodeToString(user)
}
}
Использование на Android (в Activity):
// androidApp
import com.example.shared.UserRepository
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val repo = UserRepository()
CoroutineScope(Dispatchers.Main).launch {
val user = repo.fetchUser(42)
println("User: ${user.name}, Email: ${user.email}")
}
}
}
Использование на iOS (в Swift):
// iosApp/ContentView.swift
import SwiftUI
import shared
struct ContentView: View {
var body: some View {
Text("Hello, KMP!")
.onAppear {
let repo = UserRepository()
// В iOS вызов suspend функций требует обертки
repo.fetchUser(userId: 42) { user, error in
if let user = user {
print("User: \(user.name), Email: \(user.email)")
}
}
}
}
}
Основные методы и функции Kotlin Multiplatform
Ниже приведены ключевые API и подходы, которые вы будете использовать в KMP-проектах. Мы рассмотрим их на примере библиотек kotlinx.serialization, kotlinx.coroutines и ktor-client, которые являются стандартом де-факто для KMP.
1. kotlinx.serialization.json.Json.decodeFromString()
Что делает: Десериализует строку JSON в объект указанного типа. Является основным методом для парсинга JSON в KMP.
Параметры:
jsonString: String— строка в формате JSON.- Возвращает объект типа
T, который должен быть помечен аннотацией@Serializable.
Пример:
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
@Serializable
data class Product(val id: Int, val title: String, val price: Double)
fun parseProduct(json: String): Product {
val jsonParser = Json { ignoreUnknownKeys = true }
return jsonParser.decodeFromString(json)
}
// Использование
val productJson = """{"id": 1, "title": "Kotlin Book", "price": 29.99}"""
val product = parseProduct(productJson)
println(product.title) // Kotlin Book
2. kotlinx.serialization.json.Json.encodeToString()
Что делает: Сериализует объект в строку JSON. Обратная операция к decodeFromString.
Параметры:
value: T— объект для сериализации.- Возвращает
String— JSON-представление объекта.
Пример:
fun serializeProduct(product: Product): String {
val jsonParser = Json { prettyPrint = true }
return jsonParser.encodeToString(product)
}
// Использование
val product = Product(2, "Kotlin Mug", 12.50)
val json = serializeProduct(product)
println(json)
// Вывод:
// {
// "id": 2,
// "title": "Kotlin Mug",
// "price": 12.5
// }
3. kotlinx.coroutines.delay()
Что делает: Приостанавливает выполнение корутины на заданное количество миллисекунд (или других временных единиц). Используется для имитации задержек, таймаутов или ожидания.
Параметры:
timeMillis: Long— время задержки в миллисекундах.- Также есть перегрузка с
Duration:delay(duration: Duration).
Пример:
import kotlinx.coroutines.*
suspend fun simulateNetworkCall(): String {
println("Начало запроса...")
delay(2000) // Ждем 2 секунды
println("Запрос завершен.")
return "Данные получены"
}
fun main() = runBlocking {
val result = simulateNetworkCall()
println(result)
}
4. kotlinx.coroutines.withContext()
Что делает: Позволяет переключить контекст выполнения корутины (например, на Dispatchers.IO для сетевых запросов или Dispatchers.Main для UI). Критически важен для KMP, так как платформы имеют разные главные потоки.
Параметры:
context: CoroutineContext— новый контекст (обычно один изDispatchers).block: suspend CoroutineScope.() -> T— блок кода, который будет выполнен в новом контексте.
Пример:
import kotlinx.coroutines.*
suspend fun loadData(): String = withContext(Dispatchers.IO) {
// Тяжелая операция (сеть, БД) выполняется в фоновом потоке
delay(1000)
"Данные из БД"
}
fun main() = runBlocking {
val data = loadData()
println(data)
}
5. io.ktor.client.HttpClient.get()
Что делает: Выполняет HTTP GET-запрос. Ktor — это асинхронный HTTP-клиент, полностью поддерживающий KMP.
Параметры:
urlString: String— URL для запроса.- Возвращает
HttpResponse, из которого можно получить тело ответа.
Пример:
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
val client = HttpClient()
suspend fun fetchData(): String {
val response: HttpResponse = client.get("https://api.example.com/data")
return response.bodyAsText()
}
// В реальном проекте не забывайте Также в библиотеке
Elasticsearch .NET для C# (.NET): полное руководство с примерами
Геолокация и карты в Java: библиотеки, API и примеры кода
Prometheus метрики в TypeScript: полное руководство по мониторингу