Недавно я начал экспериментировать с Kotlin Multiplatform, которая все еще находится в экспериментальном состоянии. Прежде чем я смог начать делать что-то продуктивное, я столкнулся с несколькими проблемами и хотел сообщить, как их исправить, если вы также столкнетесь с ними.
Установка пути к Android SDK
Первая проблема, с которой вы автоматически столкнетесь после создания проекта:
Please fix the ‘sdk.dir’ property in the local.properties file
Просто перейдите в Android Studio, откройте свой local.properties
и скопируйте значение sdk.dir
оттуда в соответствующий файл в многоплатформенном проекте. Теперь вы можете продолжить сборку своего проекта.
Переименование исходного набора Android
Проект создается с несколькими исходными наборами _3 _, _ 4_, iosMain
и т. Д. Однако пакеты Android называются main
и test
, что меня очень сбивает с толку - почему не androidMain
? К сожалению, простое переименование пакета приведет к ошибкам сборки.
Кроме того, я открыл app/build.gradle
и поместил это в блок android {}
:
sourceSets { main { manifest.srcFile 'src/androidMain/AndroidManifest.xml' java.srcDirs = ['src/androidMain/java'] res.srcDirs = ['src/androidMain/res'] } }
сообщая Gradle, где вместо этого он может найти код Android.
Написание тестов для классов в общем модуле
Приятно иметь возможность писать код, который будет совместно использоваться Android и iOS. Но мне тоже нужно протестировать этот код. Как и в IntelliJ, я нажал ⌘ + ⇧ + T внутри класса, чтобы создать тест для этого класса.
Я прочитал, что мне нужно использовать kotlin-test
- он уже добавлен в путь к классам, но нет возможности создать «тест Kotlin», и все другие предлагаемые для использования среды тестирования неверны. Я просто выбрал один и нажал «продолжить». К счастью, вы можете просто начать вводить @Test
, и IntelliJ попросит вас импортировать правильную библиотеку kotlin.test.Test
. Но потом я заметил, что он создал мой тест в пакете test
, который является пакетом для тестов Android, вместо пакета commonTest
.
Извлеченный урок: прямо сейчас создайте все ваши общие тесты вручную в правильном пакете.
Выполнение тестов
После того, как я наконец написал тест, я, конечно, захотел его выполнить - но это было бы слишком просто, если бы он просто работал!
В моем случае я добавил библиотеку kotlinx-serialization
, и при попытке запустить тест для общего модуля моя сборка не удалась.
Task :app:compileDebugAndroidTestKotlinAndroid FAILED e: /mpp-test/app/src/commonTest/kotlin/sample/SampleTests.kt: (3, 16): Unresolved reference: serialization
Я быстро заметил, что это не удается при компиляции тестовых ресурсов Android, но я просто хотел запустить общий код, он не имеет ничего общего с Android 🤷♂
После некоторого поиска я обнаружил, что в настоящее время это ошибка, которую необходимо решить с помощью Kotlin 1.3.40. А пока вам явно нужно добавить все мультиплатформенные зависимости, предназначенные для androidMain
, в androidTest
.
После того, как я исправил это, я столкнулся с другой проблемой: щелкнув правой кнопкой мыши на commonTest
и выбрав «Запустить все тесты», я получил:
Нет доступных задач
Я подумал: «Хорошо, тогда давайте запустим тест, щелкнув правой кнопкой мыши тестовый класс и выбрав« Выполнить »отсюда».
Это сработало, однако закончилось:
Тестовые события не получены
Глядя на вывод консоли, он пытался запустить Exeuting task ‘-tests “sample.sampleTests"'
. Он пытался выполнить… что?
Опять же, просмотрев открытые ошибки мультиплатформенности, я нашел решение:
Перейдите к Preferences | Build, Execution, Deployment | Build Tools | Gradle | Runner
, Run tests using
и выберитеPlatform Test Runner
вместо Gradle Test Runner
- теперь вы можете запускать тесты.
Обертка Gradle отсутствует
Пытался запустить что-нибудь из командной строки с помощью Gradle? В настоящее время в новые проекты не добавляется оболочка Gradle. Что касается меня, я пошел в другой проект и скопировал gradlew
, gradlew.bat
и каталог gradle/wrapper
в многоплатформенный проект. Впоследствии вы можете использовать ./gradlew build
и тому подобное.
Невозможно запустить приложение с Ktor в качестве зависимости
Если вы хотите наладить работу в сети, вам нужно использовать Ktor. Я добавил все зависимости для Multiplatform и попытался запустить свое приложение. Это не удалось с
FAILURE: Build failed with an exception. * What went wrong: Execution failed for task ‘:app:transformResourcesWithMergeJavaResForDebug’. > More than one file was found with OS independent path ‘META-INF/ktor-http.kotlin_module’
Добавьте это в свой android {}
блок:
packagingOptions { exclude 'META-INF/*.kotlin_module' }
Использование Ktor с Kotlinx. Сериализация на Kotlin / Native
Итак, вы можете добавить JsonFeature
в Ktor, что позволяет Ktor автоматически анализировать некоторый JSON, полученный из вызова API, к некоторому объекту:
install(JsonFeature) { serializer = KotlinxSerializer() }
Если вы сейчас выполните HTTP-вызов с помощью Ktor и определите тип возвращаемого значения функции как вашу сущность, все остальное Ktor сделает за вас самостоятельно:
suspend fun doHttpCall(): YourEntity { return client.get { ... } }
Это работало нормально, когда я тестировал его на Android. При реализации части iOS с использованием Kotlin / Native возникло исключение:
Получение сериализатора из KClass недоступно на родном языке из-за отсутствия отражения. «+» Используйте .serializer () непосредственно в сериализуемом классе.
В настоящее время это TODO на Kotlin / Native. Итак, просто измените свой метод примерно так:
val response = client.get<HttpResponse> { ... } val jsonBody = response.readText() return Json.parse(YourEntity.serializer(), jsonBody)
Будьте осторожны с одиночками
Модель параллелизма Kotlin / Native спроектирована таким образом, что она замораживает графы объектов, чтобы гарантировать неизменность. Подробнее об этом можно прочитать здесь.
В конце указывается, при каких обстоятельствах объекты автоматически замораживаются. Определенно я должен прочитать это заранее.
Я создал Kotlin object
с именем ServiceLocator
, который должен служить мне очень простым инструментом для внедрения зависимостей. Я поместил свой HTTP-клиент Ktor внутрь и получил исключение:
kotlin.native.concurrent.InvalidMutabilityException: попытка мутации замороженного io.ktor.client.request.HttpRequestPipeline
что было вызвано именно этой проблемой. Однако мне потребовалось много времени, чтобы понять это.
Вам нужно добавить @ThreadLocal
в свой object
или переместить его из синглтона. Однако добавление @ThreadLocal
может иметь нежелательные побочные эффекты, поэтому будьте осторожны с его использованием и не рассматривайте его как окончательное решение.
Это была проблема, на поиск и решение которой мне потребовалось время 😉 Счастливое многоплатформенное кодирование.