Spec-Zone.ru › Kotlin 1.6

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

В этом учебнике вы узнаете, как создать многоплатформенную библиотеку для платформ JVM, JS и Native, написать общие тесты для всех платформ и опубликовать библиотеку в локальном репозитории 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.

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

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

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

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

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

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 объекта Base64Factory для каждой платформы:

  • 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.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
    
    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()
        }
    }
    
  4. В терминале выполните задачу Gradle check:

    ./gradlew check 
    

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

Тесты будут выполняться на всех платформах (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 в дополнение к общим тестам.

END_OF_DOCUMENT_MARKER

Опубликовать вашу библиотеку в локальный репозиторий Maven

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

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

  1. В файле 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'
  1. В терминале выполните задачу publishToMavenLocal Gradle для публикации вашей библиотеки в локальный репозиторий Maven:

    ./gradlew publishToMavenLocal
    

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

Ваша библиотека будет опубликована в локальном репозитории 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.

Что дальше?

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

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

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

  • Создайте полное веб-приложение с Kotlin Multiplatform – практическое руководство.

Последнее изменение: 07 апреля 2022
Опубликовать многоплатформенную библиотеку Обмен кодом на платформах

© 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