Эта библиотека упрощает использование библиотек Kotlin/JS из программ Kotlin/JVM и Kotlin/Native. Это делает получение кода таким же простым, как получение данных:
Zipline работает путем встраивания механизма JavaScript QuickJS в вашу программу Kotlin/JVM или Kotlin/Native. Это небольшой и быстрый движок JavaScript, который хорошо подходит для встраивания в приложения.
(Ищете Duktape Android?)
Давайте создадим викторину, в которой каждый день будут появляться новые вопросы, даже если наши пользователи не обновляют свои приложения. Мы определяем наш интерфейс в commonMain
, чтобы мы могли вызывать его из Kotlin/JVM и реализовывать его в Kotlin/JS.
interface TriviaService : ZiplineService {
fun games (): List < TriviaGame >
fun answer ( questionId : String , answer : String ): AnswerResult
}
Далее мы реализуем это в jsMain
:
class RealTriviaService : TriviaService {
// ...
}
Давайте подключим реализацию, работающую на Kotlin/JS, к интерфейсу, работающему на Kotlin/JVM. В jsMain
мы определяем экспортируемую функцию для привязки реализации:
@JsExport
fun launchZipline () {
val zipline = Zipline .get()
zipline.bind< TriviaService >( " triviaService " , RealTriviaService ())
}
Теперь мы можем запустить сервер разработки, который будет обслуживать наш JavaScript любым запущенным приложениям, которые его запрашивают.
$ ./gradlew -p samples trivia:trivia-js:serveDevelopmentZipline --info --continuous
Обратите внимание, что этот Gradle никогда не достигнет 100%. Это ожидаемо; мы хотим, чтобы сервер разработки оставался включенным. Также обратите внимание, что флаг --continuous
будет вызывать перекомпиляцию при каждом изменении кода.
Вы можете просмотреть манифест обслуживаемого приложения по адресу localhost:8080/manifest.zipline.json. Он ссылается на все модули кода приложения.
В jvmMain
нам нужно написать программу, которая загружает наш код Kotlin/JS и вызывает его. Мы используем ZiplineLoader
, который управляет загрузкой, кэшированием и загрузкой кода. Мы создаем Dispatcher
для запуска Kotlin/JS. Это должен быть однопоточный диспетчер, поскольку каждый экземпляр Zipline должен быть ограничен одним потоком.
suspend fun launchZipline ( dispatcher : CoroutineDispatcher ): Zipline {
val manifestUrl = " http://localhost:8080/manifest.zipline.json "
val loader = ZiplineLoader (
dispatcher,
ManifestVerifier . NO_SIGNATURE_CHECKS ,
OkHttpClient (),
)
return loader.loadOnce( " trivia " , manifestUrl)
}
Теперь мы собираем и запускаем программу JVM, чтобы собрать все это воедино. Сделайте это в терминале, отдельном от сервера разработки!
$ ./gradlew -p samples trivia:trivia-host:shadowJar
java -jar samples/trivia/trivia-host/build/libs/trivia-host-all.jar
Zipline упрощает обмен интерфейсами с Kotlin/JS. Определите интерфейс в commonMain
, реализуйте его в Kotlin/JS и вызывайте его с хост-платформы. Или сделайте наоборот: реализуйте его на хост-платформе и вызывайте из Kotlin/JS.
Мостовые интерфейсы должны расширять ZiplineService
, который определяет один метод close()
для освобождения удерживаемых ресурсов.
По умолчанию аргументы и возвращаемые значения передаются по значению. Zipline использует kotlinx.serialization для кодирования и декодирования значений, передаваемых через границу.
Типы интерфейсов, наследуемые от ZiplineService
передаются по ссылке: получатель может вызывать методы в реальном экземпляре.
Функции интерфейса могут быть приостановлены. Внутри Zipline реализует setTimeout()
чтобы асинхронный код работал так, как это должно быть в Kotlin/JS.
Zipline также поддерживает Flow<T>
в качестве параметра или типа возвращаемого значения. Это упрощает создание реактивных систем.
Одним из потенциальных узких мест при внедрении JavaScript является ожидание, пока движок скомпилирует входной исходный код. Zipline предварительно компилирует JavaScript в эффективный байт-код QuickJS, чтобы устранить это снижение производительности.
Еще одно узкое место — ожидание загрузки кода. Zipline решает эту проблему с помощью поддержки модульных приложений. Каждый входной модуль (например, стандартные библиотеки Kotlin, библиотеки сериализации и сопрограммы) загружается одновременно. Каждый загруженный модуль кэшируется. Модули также могут быть встроены в хост-приложение, чтобы избежать каких-либо загрузок, если сеть недоступна. Если ваш модуль приложения меняется чаще, чем ваши библиотеки, пользователи загружают только то, что изменилось.
Если у вас возникнут проблемы с производительностью во время выполнения QuickJS, Zipline включает в себя профилировщик выборки. Вы можете использовать это, чтобы получить представление о том, как ваше приложение тратит время процессора.
Zipline реализует console.log
, пересылая сообщения на хост-платформу. Он использует android.util.Log
на Android, java.util.logging
на JVM и stdout
на Kotlin/Native.
Zipline интегрирует исходные карты Kotlin в байт-код QuickJS. Если ваш процесс выйдет из строя, трассировка стека напечатает файлы .kt
и номера строк. Несмотря на то, что внизу находится JavaScript, разработчикам не нужно взаимодействовать с файлами .js
.
После использования мостового интерфейса его необходимо закрыть, чтобы можно было удалить одноранговый объект. Это сложно сделать правильно, поэтому Zipline заимствует идеи у LeakCanary и активно обнаруживает пропущенный вызов close()
.
Zipline поддерживает подписи EdDSA Ed25519 и ECDSA P-256 для аутентификации загруженных библиотек.
Настройка проста. Создайте пару ключей EdDSA. Задача для этого устанавливается вместе с плагином Zipline Gradle.
$ ./gradlew :generateZiplineManifestKeyPairEd25519
...
---------------- ----------------------------------------------------------------
ALGORITHM: Ed25519
PUBLIC KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
PRIVATE KEY: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
---------------- ----------------------------------------------------------------
...
Поместите закрытый ключ на сервер сборки и настройте его для подписи сборок:
zipline {
signingKeys {
create( " key1 " ) {
privateKeyHex.set( .. .)
algorithmId.set(app.cash.zipline.loader. SignatureAlgorithmId . Ed25519 )
}
}
}
Поместите открытый ключ в каждое хост-приложение и настройте его для проверки подписей:
val manifestVerifier = ManifestVerifier . Builder ()
.addEd25519( " key1 " , .. .)
.build()
val loader = ZiplineLoader (
manifestVerifier = manifestVerifier,
.. .
)
И подписывание, и проверка принимают несколько ключей для поддержки ротации ключей.
Zipline предназначен для запуска кода вашей организации, когда и где вы этого захотите. Он не предлагает песочницу или изоляцию процессов и не должен использоваться для выполнения ненадежного кода.
Важно помнить, что этот дизайн предполагает неявное доверие:
Это не защищает от каких-либо компромиссов из вышеизложенного.
Также пока не предусмотрен механизм для объявления вне закона старых (подписанных) версий исполняемого кода, имеющих известные проблемы.
Есть несколько вещей, которые вы можете сделать, чтобы горячая перезагрузка выполнялась как можно быстрее:
kotlin.incremental.js.ir=true
чтобы включить инкрементальную компиляцию Kotlin/JS.org.gradle.unsafe.configuration-cache=true
чтобы включить кеш конфигурации Gradle.tasks.withType(DukatTask::class) { enabled = false }
, чтобы отключить задачу Dukat, если вы не используете объявления типов TypeScript.Zipline работает на Android 4.3+ (уровень API 18+), Java 8+ и Kotlin/Native.
Zipline использует нестабильные API в своей реализации и чувствителен к обновлениям версий этих компонентов.
Компонент | Поддерживаемая версия | Примечания |
---|---|---|
Котлин-компилятор | 2.0.0 | Плагины компилятора Kotlin пока не имеют стабильного API. |
Сериализация Котлина | 1.6.3 | Для decodeFromDynamic() , encodeToDynamic() и ContextualSerializer . |
Котлинские сопрограммы | 1.8.1 | Для transformLatest() , Deferred.getCompleted() и CoroutineStart.ATOMIC . |
Мы намерены использовать стабильные API, как только они станут доступны.
Мы намерены обеспечить совместимость версий хоста и среды выполнения Zipline, чтобы вы могли обновлять каждую из них независимо.
Хост-версия Zipline | Поддерживаемые версии Zipline во время выполнения |
---|---|
0.х | Точно такая же версия 0.x, что и хост. |
1.х | Любая версия 1.x. |
Copyright 2015 Square, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Этот проект ранее был известен как Duktape-Android и содержал движок JavaScript Duktape для Android. История Duktape по-прежнему присутствует в этом репозитории, как и теги выпуска. Доступные версии перечислены в центре Maven.