Esta biblioteca agiliza el uso de bibliotecas Kotlin/JS de programas Kotlin/JVM y Kotlin/Native. Hace que obtener código sea tan fácil como obtener datos:
Zipline funciona incorporando el motor JavaScript QuickJS en su programa Kotlin/JVM o Kotlin/Native. Es un motor JavaScript pequeño y rápido que se adapta bien a la integración en aplicaciones.
(¿Buscas Duktape Android?)
Hagamos un juego de preguntas que tenga preguntas nuevas todos los días, incluso si nuestros usuarios no actualizan sus aplicaciones. Definimos nuestra interfaz en commonMain
para que podamos llamarla desde Kotlin/JVM e implementarla en Kotlin/JS.
interface TriviaService : ZiplineService {
fun games (): List < TriviaGame >
fun answer ( questionId : String , answer : String ): AnswerResult
}
A continuación lo implementamos en jsMain
:
class RealTriviaService : TriviaService {
// ...
}
Conectemos la implementación que se ejecuta en Kotlin/JS a la interfaz que se ejecuta en Kotlin/JVM. En jsMain
definimos una función exportada para vincular la implementación:
@JsExport
fun launchZipline () {
val zipline = Zipline .get()
zipline.bind< TriviaService >( " triviaService " , RealTriviaService ())
}
Ahora podemos iniciar un servidor de desarrollo para servir nuestro JavaScript a cualquier aplicación en ejecución que lo solicite.
$ ./gradlew -p samples trivia:trivia-js:serveDevelopmentZipline --info --continuous
Tenga en cuenta que este Gradle nunca alcanzará el 100%. Eso es lo esperado; Queremos que el servidor de desarrollo permanezca encendido. También tenga en cuenta que el indicador --continuous
activará una recompilación cada vez que cambie el código.
Puede ver el manifiesto de la aplicación servida en localhost:8080/manifest.zipline.json. Hace referencia a todos los módulos de código de la aplicación.
En jvmMain
necesitamos escribir un programa que descargue nuestro código Kotlin/JS y lo llame. Usamos ZiplineLoader
que maneja la descarga, el almacenamiento en caché y la carga de código. Creamos un Dispatcher
para ejecutar Kotlin/JS. Debe ser un despachador de un solo subproceso, ya que cada instancia de Zipline debe estar confinada a un solo subproceso.
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)
}
Ahora compilamos y ejecutamos el programa JVM para unirlo todo. ¡Haga esto en una terminal separada del servidor de desarrollo!
$ ./gradlew -p samples trivia:trivia-host:shadowJar
java -jar samples/trivia/trivia-host/build/libs/trivia-host-all.jar
Zipline facilita compartir interfaces con Kotlin/JS. Defina una interfaz en commonMain
, impleméntela en Kotlin/JS y llámela desde la plataforma host. O haga lo contrario: impleméntelo en la plataforma host y llámelo desde Kotlin/JS.
Las interfaces puenteadas deben ampliar ZiplineService
, que define un único método close()
para liberar los recursos retenidos.
De forma predeterminada, los argumentos y los valores de retorno se pasan por valor. Zipline usa kotlinx.serialization para codificar y decodificar valores que pasan a través del límite.
Los tipos de interfaz que se extienden desde ZiplineService
son de paso por referencia: el receptor puede llamar a métodos en una instancia en vivo.
Es posible que se estén suspendiendo las funciones de la interfaz. Internamente, Zipline implementa setTimeout()
para hacer que el código asincrónico funcione como se supone que debe hacerlo en Kotlin/JS.
Zipline también admite Flow<T>
como parámetro o tipo de retorno. Esto facilita la construcción de sistemas reactivos.
Un posible cuello de botella al incorporar JavaScript es esperar a que el motor compile el código fuente de entrada. Zipline precompila JavaScript en un código de bytes QuickJS eficiente para eliminar esta penalización de rendimiento.
Otro cuello de botella está esperando que se descargue el código. Zipline aborda esto con soporte para aplicaciones modulares. Cada módulo de entrada (como las bibliotecas estándar, de serialización y de rutinas de Kotlin) se descarga simultáneamente. Cada módulo descargado se almacena en caché. Los módulos también se pueden integrar con la aplicación host para evitar descargas si la red es inaccesible. Si el módulo de su aplicación cambia con más frecuencia que sus bibliotecas, los usuarios solo descargan lo que ha cambiado.
Si tiene problemas de rendimiento en el tiempo de ejecución de QuickJS, Zipline incluye un generador de perfiles de muestreo. Puede utilizar esto para obtener un desglose de cómo su aplicación utiliza su tiempo de CPU.
Zipline implementa console.log
reenviando mensajes a la plataforma anfitriona. Utiliza android.util.Log
en Android, java.util.logging
en JVM y stdout
en Kotlin/Native.
Zipline integra mapas fuente de Kotlin en el código de bytes QuickJS. Si su proceso falla, stacktrace imprimirá archivos .kt
y números de línea. Aunque hay JavaScript debajo, los desarrolladores no necesitan interactuar con archivos .js
.
Después de usar una interfaz puenteada, se debe cerrar para que el objeto del mismo nivel pueda ser recolectado como basura. Esto es difícil de hacer bien, por lo que Zipline toma prestadas ideas de LeakCanary y detecta agresivamente cuando se pierde una llamada close()
.
Zipline admite firmas EdDSA Ed25519 y ECDSA P-256 para autenticar bibliotecas descargadas.
La configuración es sencilla. Genere un par de claves EdDSA. Se instala una tarea para esto con el complemento Zipline Gradle.
$ ./gradlew :generateZiplineManifestKeyPairEd25519
...
---------------- ----------------------------------------------------------------
ALGORITHM: Ed25519
PUBLIC KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
PRIVATE KEY: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
---------------- ----------------------------------------------------------------
...
Coloque la clave privada en el servidor de compilación y configúrelo para firmar compilaciones:
zipline {
signingKeys {
create( " key1 " ) {
privateKeyHex.set( .. .)
algorithmId.set(app.cash.zipline.loader. SignatureAlgorithmId . Ed25519 )
}
}
}
Coloque la clave pública en cada aplicación host y configúrela para verificar firmas:
val manifestVerifier = ManifestVerifier . Builder ()
.addEd25519( " key1 " , .. .)
.build()
val loader = ZiplineLoader (
manifestVerifier = manifestVerifier,
.. .
)
Tanto la firma como la verificación aceptan múltiples claves para admitir la rotación de claves.
Zipline está diseñado para ejecutar el código de su organización cuando y donde lo desee. No ofrece un espacio aislado ni aislamiento de procesos y no debe usarse para ejecutar código que no sea de confianza.
Es fundamental tener en cuenta que este diseño pone confianza implícita en:
No protege contra ningún tipo de compromiso de lo anterior.
Además, todavía no proporciona un mecanismo para prohibir versiones antiguas (firmadas) de código ejecutable que tengan problemas conocidos.
Hay algunas cosas que puede hacer para asegurarse de que la recarga en caliente se ejecute lo más rápido posible:
kotlin.incremental.js.ir=true
para habilitar la compilación incremental de Kotlin/JS.org.gradle.unsafe.configuration-cache=true
para habilitar el caché de configuración de Gradle.tasks.withType(DukatTask::class) { enabled = false }
para desactivar la tarea Dukat si no está utilizando declaraciones de tipo TypeScript.Zipline funciona en Android 4.3+ (nivel de API 18+), Java 8+ y Kotlin/Native.
Zipline utiliza API inestables en su implementación y es sensible a las actualizaciones de versión de estos componentes.
Componente | Versión compatible | Notas |
---|---|---|
Compilador Kotlin | 2.0.0 | Los complementos del compilador de Kotlin aún no tienen una API estable. |
Serialización de Kotlin | 1.6.3 | Para decodeFromDynamic() , encodeToDynamic() y ContextualSerializer . |
Corrutinas de Kotlin | 1.8.1 | Para transformLatest() , Deferred.getCompleted() y CoroutineStart.ATOMIC . |
Tenemos la intención de utilizar API estables tan pronto como estén disponibles.
Tenemos la intención de mantener interoperables las versiones de host y tiempo de ejecución de Zipline para que pueda actualizar cada una de forma independiente.
Versión de tirolesa del anfitrión | Versiones compatibles de Runtime Zipline |
---|---|
0.x | Exactamente la misma versión 0.x que el host. |
1.x | Cualquier versión 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.
Este proyecto se conocía anteriormente como Duktape-Android y empaquetaba el motor JavaScript Duktape para Android. El historial de Duktape todavía está presente en este repositorio, al igual que las etiquetas de lanzamiento. Las versiones disponibles se enumeran en Maven central.