Какие знаешь особенности common code в Kotlin Multiplatform?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Особенности написания common code в Kotlin Multiplatform
Разработка common code (общего кода) в Kotlin Multiplatform (KMP) — это основа для создания кросс-платформенных приложений. Этот код размещается в shared модуле и предназначен для компиляции на все целевые платформы (Android, iOS, Desktop, Web). Его особенности напрямую связаны с философией KMP: максимальная переиспользуемость при сохранении гибкости для платформенных реализаций.
1. Ограничения и совместимость
Common код должен быть строго platform-independent. Это означает:
- Отсутствие прямых зависимостей от платформенных SDK: нельзя напрямую использовать классы из Android SDK (
android.view.View) или iOS Foundation (NSString). - Использование только Kotlin Standard Library и общих многоплатформенных библиотек: доступны
kotlin.collections,kotlin.coroutines,kotlin.io(в базовом виде), а также библиотеки от JetBrains и community (kotlinx.serialization,kotlinx.coroutines,ktorдля сетевых запросов в common части).
Пример недопустимого кода в common модуле:
// НЕВЕРНО - платформенный класс
import android.widget.Toast
fun showMessage(message: String) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
Пример верного подхода:
// Объявляем ожидаемый declaration (expect) в common коде
expect fun showMessage(message: String)
// Затем реализуем (actual) в платформенных модулях
// Для Android:
actual fun showMessage(message: String) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
// Для iOS:
actual fun showMessage(message: String) {
// Используем iOS API через cinterop или другие механизмы
}
2. Механизм expect/actual
Это центральная концепция для работы с платформенными особенностями.
expectdeclaration: в common коде объявляется функция, класс, свойство или даже тип, который должен быть реализован на каждой платформе. Это контракт.actualimplementation: в каждом платформенном модуле (androidMain, iosMain) предоставляется реализация, использующая нативные API.
Пример с типом:
// В commonMain
expect class PlatformDateTime() {
fun getCurrentTime(): String
}
// В androidMain
import java.text.SimpleDateFormat
import java.util.Date
actual class PlatformDateTime {
actual fun getCurrentTime(): String {
val formatter = SimpleDateFormat("HH:mm:ss")
return formatter.format(Date())
}
}
// В iosMain
import kotlinx.datetime.*
actual class PlatformDateTime {
actual fun getCurrentTime(): String {
// Используем библиотеку kotlinx.datetime, которая также multiplatform
val current = Clock.System.now()
return current.toString()
}
}
3. Организация исходного кода и зависимости
- Структура модуля: В shared модуле существуют директории
commonMain,androidMain,iosMain,desktopMainи т.д. Common код живет только вcommonMain. - Gradle зависимости в commonMain: В
build.gradle.ktsобщего модуля зависимости дляcommonMainуказываются в блокеsourceSets.
kotlin {
sourceSets {
commonMain.dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.0")
implementation("io.ktor:ktor-client-core:2.3.0")
}
androidMain.dependencies {
// Здесь можно добавить Android-specific зависимости
}
}
}
- Использование многоплатформенных библиотек: Ключевой момент. Библиотеки типа
kotlinx.coroutines,ktor,kotlinx.serializationсами используют механизмexpect/actual, предоставляя общий API в common и нативные реализации для платформ.
4. Особенности работы с ресурсами и файлами
- Работа с файлами: Используется общий API
kotlin.io, но реальные пути и доступ к файловой системе сильно различаются. Для сложных операций часто требуетсяexpect/actual. - Ресурсы (изображения, строки локализации): Не имеют единого механизма в common коде. Часто используются следующие подходы:
* **Общие данные**: хранить raw данные (JSON, XML) и парсить их в common коде.
* **Платформенные ресурсы**: объявить `expect fun getImageResource(id: String)` и загружать ресурсы через платформенные API (Android `Resources`, iOS `UIImage`).
* **Сторонние решения**: библиотеки, например, для локализации, которые сами являются multiplatform.
5. Тестирование common кода
- Common тесты: можно писать тесты в
commonTest, используяkotlin.test. Эти тесты будут выполняться на всех платформах.
import kotlin.test.Test
import kotlin.test.assertTrue
class CommonLogicTest {
@Test
fun testCommonCalculation() {
val result = CommonCalculator().add(2, 3)
assertTrue(result == 5)
}
}
- Mocking платформенных
actualреализаций: Для тестирования common кода, который зависит отexpectdeclarations, нужно предоставитьactualреализации в тестовой среде. Это может быть упрощенная "mock" реализация вcommonTestили использование специальных тестовых source sets.
6. Оптимизации и производительность
- Избегание тяжелых вычислений в common коде: Common код компилируется в разные цели (JVM bytecode, JavaScript, Native). Следует избегать операций, которые могут быть эффективны на одной платформе, но дороги на другой.
- Использование
inlineклассов и функций: Помогает уменьшить overhead, особенно при компиляции в Native. - Внимание к memory моделям: Kotlin/Native имеет строгую memory модель с запретом shared mutable state между потоками. При использовании
kotlinx.coroutinesв common коде для iOS цели нужно учитывать это (использоватьDispatchers.Mainдля UI, избегать неконтролируемого shared state).
Вывод: Common код в KMP — это тщательно продуманный интерфейс и бизнес-логика, абстрагированные от платформ через механизм expect/actual. Его успешная разработка требует дисциплины в соблюдении границ, глубокого понимания доступных многоплатформенных библиотек и продуманной архитектуры, которая позволит максимально вынести в common модуль, оставив платформам только необходимые реализации UI и низкоуровневых системных операций.