Spec-Zone.ru › Kotlin 1.7

Создание и публикация многоплатформенной библиотеки — учебник

В этом учебнике вы узнаете, как создать многоплатформенную библиотеку для JVM, JS и нативных платформ, написать общие тесты для всех платформ и опубликовать библиотеку в локальном репозитории Maven.

Эта библиотека преобразует исходные данные — строки и массивы байтов — в формат Base64. Она может быть использована на Kotlin/JVM, Kotlin/JS и любой доступной платформе Kotlin/Native.

Вы будете использовать различные способы реализации преобразования в формат Base64 на разных платформах:

  • Для JVM — класс java.util.Base64.

  • Для JS — функция btoa().

  • Для Kotlin/Native — ваша собственная реализация.

Вы также протестируете свой код с помощью общих тестов и затем опубликуете библиотеку в вашем локальном репозитории Maven.

Настройка среды

Вы можете выполнить этот учебник на любой операционной системе. Скачайте и установите последнюю версию IntelliJ IDEA с последним плагином Kotlin.

Создание проекта

  1. В IntelliJ IDEA выберите Файл | Новый | Проект.

  2. В левой панели выберите Kotlin Multiplatform.

  3. Введите имя проекта, затем в разделе Многоплатформенный выберите Библиотека в качестве шаблона проекта.

    Select a project template
  4. Выберите DSL Gradle – Kotlin или Groovy.

  5. Укажите JDK, который необходим для разработки проектов Kotlin.

  6. Нажмите Далее, а затем Готово.

Дополнительная настройка проекта

Для более сложных проектов, возможно, потребуется добавить больше модулей и целей:

  • Для добавления модулей выберите Проект и нажмите на значок +. Выберите тип модуля.

  • Для добавления целевых платформ выберите библиотеку и нажмите на значок +. Выберите целевую платформу.

  • Настройте параметры целевой платформы, такие как версия целевой JVM и фреймворк для тестирования.

    Configure the project
  • При необходимости укажите зависимости между модулями:

    • Многоплатформенные и Android модули

    • Многоплатформенные и iOS модули

    • Модули JVM

    Configure the project

Мастер создаст пример многоплатформенной библиотеки со следующей структурой:

Multiplatform library structure

Написание кроссплатформенного кода

Определите классы и интерфейсы, которые вы собираетесь реализовать в общем коде.

  1. В директории commonMain/kotlin создайте пакет org.jetbrains.base64.

  2. Создайте файл Base64.kt в новом пакете.

  3. Определите интерфейс Base64Encoder, который преобразует байты в формат Base64:

    package org.jetbrains.base64
    
    interface Base64Encoder {
        fun encode(src: ByteArray): ByteArray
    }
    
  4. Определите объект Base64Factory для предоставления экземпляра интерфейса Base64Encoder общему коду:

    expect object Base64Factory {
        fun createEncoder(): Base64Encoder
    }
    

Объект-фабрика помечен ключевым словом expect в кроссплатформенном коде. Для каждой платформы вы должны предоставить реализацию actual объекта Base64Factory с платформоспецифическим кодером. Подробнее о платформоспецифических реализациях.

Предоставление платформоспецифических реализаций

Теперь вы создадите платформоспецифические реализации объекта actual для каждой платформы:

  • JVM

  • JS

  • Native

JVM

  1. В директории jvmMain/kotlin создайте пакет org.jetbrains.base64.

  2. Создайте файл Base64.kt в новом пакете.

  3. Предоставьте простую реализацию объекта Base64Factory, которая делегирует классу java.util.Base64:

    Инспекции IDEA помогают создать реализации actual для объявления expect.

    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.

  1. В директории jsMain/kotlin создайте пакет org.jetbrains.base64.

  2. Создайте файл Base64.kt в новом пакете.

  3. Предоставьте простую реализацию объекта 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 нет сторонней реализации, поэтому вам нужно написать её самостоятельно.

  1. В директории nativeMain/kotlin создайте пакет org.jetbrains.base64.

  2. Создайте файл Base64.kt в новом пакете.

  3. Предоставьте свою собственную реализацию для объекта 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.toInt().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, основанный на строках, который можно покрыть базовыми тестами.

  1. В каталоге commonTest/kotlin создайте пакет org.jetbrains.base64.

  2. Создайте файл Base64Test.kt в новом пакете.

  3. Добавьте тесты в этот файл:

    package org.jetbrains.base64
    
    import kotlin.test.Test
    import kotlin.test.assertEquals
    
    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).code.toByte()
        }
    }
    
  4. В терминале выполните задачу Gradle check:

    ./gradlew check 
    

    Вы также можете запустить задачу Gradle check, дважды щелкнув её в списке задач Gradle.

Тесты будут выполняться на всех платформах (JVM, JS и Native).

Добавление платформенно-специфичных тестов

Вы также можете добавить тесты, которые будут выполняться только для определённой платформы. Например, вы можете добавить тесты UTF-16 на JVM:

  1. В каталоге jvmTest/kotlin создайте пакет org.jetbrains.base64.

  2. Создайте файл Base64Test.kt в новом пакете.

  3. Добавьте тесты в этот файл:

    package org.jetbrains.base64
    
    import kotlin.test.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

Ваша кроссплатформенная библиотека готова к публикации, чтобы вы могли использовать её в других проектах.

Для публикации библиотеки используйте плагин Gradle maven-publish.

  1. В файле build.gradle(.kts) примените плагин maven-publish и укажите группу и версию вашей библиотеки:

    plugins {
        kotlin("multiplatform") version "1.7.20"
        id("maven-publish")
    }
    
    group = "org.jetbrains.base64"
    version = "1.0.0"
    
    plugins {
       id 'org.jetbrains.kotlin.multiplatform' version '1.7.20'
       id 'maven-publish'
    }
    
    group = 'org.jetbrains.base64'
    version = '1.0.0'
    
  2. В терминале запустите задачу Gradle publishToMavenLocal, чтобы опубликовать вашу библиотеку в локальный репозиторий Maven:

    ./gradlew publishToMavenLocal
    

    Вы также можете запустить задачу Gradle publishToMavenLocal, дважды щелкнув её в списке задач Gradle.

Ваша библиотека будет опубликована в локальном репозитории Maven.

Добавление зависимости на опубликованную библиотеку

Теперь вы можете добавить вашу библиотеку в другие кроссплатформенные проекты в качестве зависимости.

Добавьте репозиторий mavenLocal() и добавьте зависимость от вашей библиотеки в файл build.gradle(.kts).

repositories {
   mavenCentral()
   mavenLocal()
}

kotlin {
   sourceSets {
      val commonMain by getting {
         dependencies {
            implementation("org.jetbrains.base64:multiplatform-lib:1.0.0")
         }
      }
   }
}
repositories {
   mavenCentral()
   mavenLocal()
}

kotlin {
   sourceSets {
      commonMain {
         dependencies {
            implementation 'org.jetbrains.base64:multiplatform-lib:1.0.0'
         }
      }
   }
}

Зависимость implementation состоит из:

  • Идентификатора группы и версии — указанных ранее в файле build.gradle(.kts)

  • Идентификатора артефакта — по умолчанию это имя вашего проекта, указанное в файле settings.gradle(.kts)

Для получения более подробной информации см. документацию Gradle по плагину maven-publish.

Что дальше?

  • Узнайте больше о публикации кроссплатформенных библиотек.

  • Узнайте больше о Kotlin Multiplatform.

  • Создайте своё первое кроссплатформенное мобильное приложение — учебник.

  • Разработайте полное приложение для веб-приложений с помощью Kotlin Multiplatform — учебник.

Последнее изменение: 06 сентября 2022
Разработайте полное приложение для веб-приложений с помощью Kotlin Multiplatform Публикация кроссплатформенных библиотек

© 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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API