Создание и публикация многоплатформенной библиотеки – учебник
В этом учебнике вы узнаете, как создать многоплатформенную библиотеку для платформ JVM, JS и Native, написать общие тесты для всех платформ и опубликовать библиотеку в локальном репозитории Maven.
Эта библиотека преобразует исходные данные – строки и массивы байтов – в формат Base64. Она может быть использована в Kotlin/JVM, Kotlin/JS и на любой доступной платформе Kotlin/Native.
Вы будете использовать разные способы реализации преобразования в формат Base64 на разных платформах:
Для JVM – класс
java.util.Base64.Для JS – функция
btoa().Для Kotlin/Native – собственная реализация.
Вы также протестируете свой код с помощью общих тестов, а затем опубликуете библиотеку в локальном репозитории Maven.
Настройка среды
Вы можете выполнить этот учебник на любой операционной системе. Скачайте и установите последнюю версию IntelliJ IDEA с последним плагином Kotlin.
Создание проекта
В IntelliJ IDEA выберите Файл | Новый | Проект.
В левой панели выберите Kotlin.
-
Введите имя проекта, затем в разделе Многоплатформенное выберите Библиотека в качестве шаблона проекта.
Выберите Gradle DSL – Kotlin или Groovy.
Укажите JDK, который необходим для разработки проектов на Kotlin.
Нажмите Далее, а затем Готово.
Мастер создаст пример многоплатформенной библиотеки со следующей структурой:
Написание кроссплатформенного кода
Определите классы и интерфейсы, которые вы собираетесь реализовать в общем коде.
В каталоге
commonMain/kotlin, создайте пакетorg.jetbrains.base64.Создайте файл
Base64.ktв новом пакете.-
Определите интерфейс
Base64Encoder, который преобразует байты в форматBase64:package org.jetbrains.base64 interface Base64Encoder { fun encode(src: ByteArray): ByteArray } -
Определите объект
Base64Factory, чтобы предоставить экземпляр интерфейсаBase64Encoderобщему коду:expect object Base64Factory { fun createEncoder(): Base64Encoder }
Объект-фабрика помечен ключевым словом expect в кроссплатформенном коде. Для каждой платформы вы должны предоставить реализацию actual объекта Base64Factory с платформенно-специфическим кодировщиком. Узнайте больше о реализациях, специфичных для платформы.
Предоставление реализаций, специфичных для платформы
Теперь вы создадите реализации actual объекта Base64Factory для каждой платформы:
JVM
В каталоге
jvmMain/kotlin, создайте пакетorg.jetbrains.base64.Создайте файл
Base64.ktв новом пакете.-
Предоставьте простую реализацию объекта
Base64Factory, которая делегирует классуjava.util.Base64:package org.jetbrains.base64 import java.util.* actual object Base64Factory { actual fun createEncoder(): Base64Encoder = JvmBase64Encoder } object JvmBase64Encoder : Base64Encoder { override fun encode(src: ByteArray): ByteArray = Base64.getEncoder().encode(src) }
Довольно просто, да? Вы предоставили платформенно-специфическую реализацию, используя прямое делегирование сторонней реализации.
JS
Реализация JS будет очень похожа на JVM.
В каталоге
jsMain/kotlin, создайте пакетorg.jetbrains.base64.Создайте файл
Base64.ktв новом пакете.-
Предоставьте простую реализацию объекта
Base64Factory, которая делегирует функцииbtoa().package org.jetbrains.base64 import kotlinx.browser.window actual object Base64Factory { actual fun createEncoder(): Base64Encoder = JsBase64Encoder } object JsBase64Encoder : Base64Encoder { override fun encode(src: ByteArray): ByteArray { val string = src.decodeToString() val encodedString = window.btoa(string) return encodedString.encodeToByteArray() } }
Native
К сожалению, для всех целей Kotlin/Native нет сторонней реализации, поэтому вам нужно написать её самостоятельно.
В каталоге
nativeMain/kotlin, создайте пакетorg.jetbrains.base64.Создайте файл
Base64.ktв новом пакете.-
Предоставьте собственную реализацию объекта
Base64Factory:package org.jetbrains.base64 private val BASE64_ALPHABET: String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" private val BASE64_MASK: Byte = 0x3f private val BASE64_PAD: Char = '=' private val BASE64_INVERSE_ALPHABET = IntArray(256) { BASE64_ALPHABET.indexOf(it.toChar()) } private fun Int.toBase64(): Char = BASE64_ALPHABET[this] actual object Base64Factory { actual fun createEncoder(): Base64Encoder = NativeBase64Encoder } object NativeBase64Encoder : Base64Encoder { override fun encode(src: ByteArray): ByteArray { fun ByteArray.getOrZero(index: Int): Int = if (index >= size) 0 else get(index).toInt() // 4n / 3 is expected Base64 payload val result = ArrayList<Byte>(4 * src.size / 3) var index = 0 while (index < src.size) { val symbolsLeft = src.size - index val padSize = if (symbolsLeft >= 3) 0 else (3 - symbolsLeft) * 8 / 6 val chunk = (src.getOrZero(index) shl 16) or (src.getOrZero(index + 1) shl 8) or src.getOrZero(index + 2) index += 3 for (i in 3 downTo padSize) { val char = (chunk shr (6 * i)) and BASE64_MASK.toInt() result.add(char.toBase64().code.toByte()) } // Fill the pad with '=' repeat(padSize) { result.add(BASE64_PAD.code.toByte()) } } return result.toByteArray() } }
Тестирование вашей библиотеки
Теперь, когда у вас есть реализации actual объекта Base64Factory для всех платформ, пришло время протестировать вашу многоплатформенную библиотеку.
Чтобы сэкономить время на тестировании, вы можете написать общие тесты, которые будут выполняться на всех платформах вместо тестирования каждой платформы по отдельности.
Предварительные условия
Перед написанием тестов добавьте метод encodeToString с реализацией по умолчанию в интерфейс Base64Encoder, который определён в commonMain/kotlin/org/jetbrains/base64/Base64.kt. Эта реализация преобразует массивы байтов в строки, что намного проще тестировать.
interface Base64Encoder {
fun encode(src: ByteArray): ByteArray
fun encodeToString(src: ByteArray): String {
val encoded = encode(src)
return buildString(encoded.size) {
encoded.forEach { append(it.toChar()) }
}
}
}
Вы также можете предоставить более эффективную реализацию этого метода для определённой платформы, например, для JVM в jvmMain/kotlin/org/jetbrains/base64/Base64.kt:
object JvmBase64Encoder : Base64Encoder {
override fun encode(src: ByteArray): ByteArray = Base64.getEncoder().encode(src)
override fun encodeToString(src: ByteArray): String = Base64.getEncoder().encodeToString(src)
}
Одним из преимуществ многоплатформенной библиотеки является наличие реализации по умолчанию с необязательными платформенно-специфическими переопределениями.
Написание общих тестов
Теперь у вас есть API на основе строк, который можно покрыть базовыми тестами.
В каталоге
commonTest/kotlin, создайте пакетorg.jetbrains.base64.Создайте файл
Base64Test.ktв новом пакете.-
Добавьте тесты в этот файл:
package org.jetbrains.base64 import kotlin.test.Test class Base64Test { @Test fun testEncodeToString() { checkEncodeToString("Kotlin is awesome", "S290bGluIGlzIGF3ZXNvbWU=") } @Test fun testPaddedStrings() { checkEncodeToString("", "") checkEncodeToString("1", "MQ==") checkEncodeToString("22", "MjI=") checkEncodeToString("333", "MzMz") checkEncodeToString("4444", "NDQ0NA==") } private fun checkEncodeToString(input: String, expectedOutput: String) { assertEquals(expectedOutput, Base64Factory.createEncoder().encodeToString(input.asciiToByteArray())) } private fun String.asciiToByteArray() = ByteArray(length) { get(it).toByte() } } -
В терминале выполните задачу Gradle
check:./gradlew check
Тесты будут выполняться на всех платформах (JVM, JS и Native).
Добавление платформенно-специфических тестов
Вы также можете добавить тесты, которые будут выполняться только для определённой платформы. Например, вы можете добавить тесты UTF-16 на JVM. Просто следуйте тем же шагам, что и для общих тестов, но создайте файл Base64Test в jvmTest/kotlin/org/jetbrains/base64:
package org.jetbrains.base64
import org.junit.Test
import kotlin.test.assertEquals
class Base64JvmTest {
@Test
fun testNonAsciiString() {
val utf8String = "Gödel"
val actual = Base64Factory.createEncoder().encodeToString(utf8String.toByteArray())
assertEquals("R8O2ZGVs", actual)
}
}
Этот тест автоматически запустится на платформе JVM в дополнение к общим тестам.
Опубликовать вашу библиотеку в локальный репозиторий Maven
Ваша многоплатформенная библиотека готова к публикации, чтобы вы могли использовать её в других проектах.
Для публикации вашей библиотеки используйте плагин maven-publish Gradle.
В файле
build.gradle(.kts), примените плагинmaven-publishи укажите группу и версию вашей библиотеки:
plugins {
kotlin("multiplatform") version "1.6.20"
id("maven-publish")
}
group = "org.jetbrains.base64"
version = "1.0.0"
plugins {
id 'org.jetbrains.kotlin.multiplatform' version '1.6.20'
id 'maven-publish'
}
group = 'org.jetbrains.base64'
version = '1.0.0'
-
В терминале выполните задачу
publishToMavenLocalGradle для публикации вашей библиотеки в локальный репозиторий Maven:./gradlew publishToMavenLocal
Ваша библиотека будет опубликована в локальном репозитории Maven.
Добавление зависимости на опубликованную библиотеку
Теперь вы можете добавить вашу библиотеку в другие многоплатформенные проекты в качестве зависимости.
Добавьте репозиторий mavenLocal() и добавьте зависимость на вашу библиотеку в файл build.gradle(.kts).
repositories {
mavenCentral()
mavenLocal()
}
kotlin {
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.base64:Base64:1.0.0")
}
}
}
}
repositories {
mavenCentral()
mavenLocal()
}
kotlin {
sourceSets {
commonMain {
dependencies {
implementation 'org.jetbrains.base64:Base64:1.0.0'
}
}
}
}
Заключение
В этом руководстве вы:
Создали многоплатформенную библиотеку с платформа-специфичными реализациями.
Написали общие тесты, которые выполняются на всех платформах.
Опубликовали вашу библиотеку в локальный репозиторий Maven.
Что дальше?
© 2010–2022 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/multiplatform-library.html