Cette bibliothèque rationalise l'utilisation des bibliothèques Kotlin/JS des programmes Kotlin/JVM et Kotlin/Native. Cela rend la récupération de code aussi simple que la récupération de données :
Zipline fonctionne en intégrant le moteur JavaScript QuickJS dans votre programme Kotlin/JVM ou Kotlin/Native. Il s'agit d'un moteur JavaScript petit et rapide, bien adapté à l'intégration dans des applications.
(Vous cherchez Duktape Android ?)
Créons un jeu-questionnaire qui propose chaque jour de nouvelles questions, même si nos utilisateurs ne mettent pas à jour leurs applications. Nous définissons notre interface dans commonMain
afin de pouvoir l'appeler depuis Kotlin/JVM et l'implémenter dans Kotlin/JS.
interface TriviaService : ZiplineService {
fun games (): List < TriviaGame >
fun answer ( questionId : String , answer : String ): AnswerResult
}
Ensuite, nous l'implémentons dans jsMain
:
class RealTriviaService : TriviaService {
// ...
}
Connectons l'implémentation exécutée dans Kotlin/JS à l'interface exécutée dans Kotlin/JVM. Dans jsMain
nous définissons une fonction exportée pour lier l'implémentation :
@JsExport
fun launchZipline () {
val zipline = Zipline .get()
zipline.bind< TriviaService >( " triviaService " , RealTriviaService ())
}
Nous pouvons maintenant démarrer un serveur de développement pour servir notre JavaScript à toutes les applications en cours d'exécution qui le demandent.
$ ./gradlew -p samples trivia:trivia-js:serveDevelopmentZipline --info --continuous
Notez que ce Gradle n'atteindra jamais 100 %. C'est prévu ; nous voulons que le serveur de développement reste allumé. Notez également que l'indicateur --continuous
déclenchera une recompilation chaque fois que le code change.
Vous pouvez voir le manifeste de l'application servi sur localhost:8080/manifest.zipline.json. Il référence tous les modules de code de l'application.
Dans jvmMain
nous devons écrire un programme qui télécharge notre code Kotlin/JS et l'appelle. Nous utilisons ZiplineLoader
qui gère le téléchargement, la mise en cache et le chargement du code. Nous créons un Dispatcher
sur lequel exécuter Kotlin/JS. Il doit s'agir d'un répartiteur à thread unique car chaque instance Zipline doit être confinée à un seul thread.
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)
}
Maintenant, nous construisons et exécutons le programme JVM pour tout assembler. Faites cela dans un terminal distinct du serveur de développement !
$ ./gradlew -p samples trivia:trivia-host:shadowJar
java -jar samples/trivia/trivia-host/build/libs/trivia-host-all.jar
Zipline facilite le partage d'interfaces avec Kotlin/JS. Définissez une interface dans commonMain
, implémentez-la dans Kotlin/JS et appelez-la depuis la plateforme hôte. Ou faites l'inverse : implémentez-le sur la plateforme hôte et appelez-le depuis Kotlin/JS.
Les interfaces pontées doivent étendre ZiplineService
, qui définit une seule méthode close()
pour libérer les ressources détenues.
Par défaut, les arguments et les valeurs de retour sont transmis par valeur. Zipline utilise kotlinx.serialization pour encoder et décoder les valeurs transmises à travers la frontière.
Les types d'interface qui s'étendent à partir de ZiplineService
sont passés par référence : le récepteur peut appeler des méthodes sur une instance en direct.
Les fonctions d'interface peuvent être suspendues. En interne, Zipline implémente setTimeout()
pour faire fonctionner le code asynchrone comme il est censé le faire dans Kotlin/JS.
Zipline prend également en charge Flow<T>
comme paramètre ou type de retour. Cela facilite la création de systèmes réactifs.
Un goulot d'étranglement potentiel lors de l'intégration de JavaScript est l'attente que le moteur compile le code source d'entrée. Zipline précompile JavaScript en bytecode QuickJS efficace pour éliminer cette pénalité de performances.
Un autre goulot d'étranglement est l'attente du téléchargement du code. Zipline résout ce problème avec la prise en charge des applications modulaires. Chaque module d'entrée (comme les bibliothèques standard, de sérialisation et de coroutines de Kotlin) est téléchargé simultanément. Chaque module téléchargé est mis en cache. Les modules peuvent également être intégrés à l'application hôte pour éviter tout téléchargement si le réseau est inaccessible. Si votre module d'application change plus fréquemment que vos bibliothèques, les utilisateurs téléchargent uniquement ce qui a été modifié.
Si vous rencontrez des problèmes de performances dans le runtime QuickJS, Zipline inclut un profileur d'échantillonnage. Vous pouvez l'utiliser pour obtenir une analyse détaillée de la façon dont votre application utilise son temps CPU.
Zipline implémente console.log
en transférant les messages vers la plate-forme hôte. Il utilise android.util.Log
sur Android, java.util.logging
sur JVM et stdout
sur Kotlin/Native.
Zipline intègre les cartes sources Kotlin dans le bytecode QuickJS. Si votre processus plante, le stacktrace imprimera les fichiers .kt
et les numéros de ligne. Même s'il y a du JavaScript en dessous, les développeurs n'ont pas besoin de s'interfacer avec les fichiers .js
.
Après avoir utilisé une interface pontée, elle doit être fermée afin que l'objet homologue puisse être récupéré. C'est difficile à réaliser, c'est pourquoi Zipline emprunte des idées à LeakCanary et détecte de manière agressive lorsqu'un appel close()
est manqué.
Zipline prend en charge les signatures EdDSA Ed25519 et ECDSA P-256 pour authentifier les bibliothèques téléchargées.
La configuration est simple. Générez une paire de clés EdDSA. Une tâche pour cela est installée avec le plugin Zipline Gradle.
$ ./gradlew :generateZiplineManifestKeyPairEd25519
...
---------------- ----------------------------------------------------------------
ALGORITHM: Ed25519
PUBLIC KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
PRIVATE KEY: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
---------------- ----------------------------------------------------------------
...
Placez la clé privée sur le serveur de build et configurez-la pour signer les builds :
zipline {
signingKeys {
create( " key1 " ) {
privateKeyHex.set( .. .)
algorithmId.set(app.cash.zipline.loader. SignatureAlgorithmId . Ed25519 )
}
}
}
Mettez la clé publique dans chaque application hôte et configurez-la pour vérifier les signatures :
val manifestVerifier = ManifestVerifier . Builder ()
.addEd25519( " key1 " , .. .)
.build()
val loader = ZiplineLoader (
manifestVerifier = manifestVerifier,
.. .
)
La signature et la vérification acceptent plusieurs clés pour prendre en charge la rotation des clés.
Zipline est conçu pour exécuter le code de votre organisation quand et où vous le souhaitez. Il n'offre pas de bac à sable ni d'isolation de processus et ne doit pas être utilisé pour exécuter du code non fiable.
Il est essentiel de garder à l’esprit que cette conception accorde une confiance implicite à :
Il ne protège pas contre toute forme de compromission de ce qui précède.
De plus, il ne fournit pas encore de mécanisme pour interdire les anciennes versions (signées) du code exécutable qui présentent des problèmes connus.
Il y a quelques choses que vous pouvez faire pour vous assurer que le rechargement à chaud s'exécute aussi rapidement que possible :
kotlin.incremental.js.ir=true
pour activer la compilation incrémentielle Kotlin/JS.org.gradle.unsafe.configuration-cache=true
pour activer le cache de configuration Gradle.tasks.withType(DukatTask::class) { enabled = false }
pour désactiver la tâche Dukat si vous n'utilisez pas de déclarations de type TypeScript.Zipline fonctionne sur Android 4.3+ (niveau API 18+), Java 8+ et Kotlin/Native.
Zipline utilise des API instables dans son implémentation et est sensible aux mises à jour de version de ces composants.
Composant | Version prise en charge | Remarques |
---|---|---|
Compilateur Kotlin | 2.0.0 | Les plugins du compilateur Kotlin n'ont pas encore d'API stable. |
Sérialisation Kotlin | 1.6.3 | Pour decodeFromDynamic() , encodeToDynamic() et ContextualSerializer . |
Coroutines Kotlin | 1.8.1 | Pour transformLatest() , Deferred.getCompleted() et CoroutineStart.ATOMIC . |
Nous avons l'intention d'utiliser des API stables dès qu'elles seront disponibles.
Nous avons l'intention de maintenir l'interopérabilité des versions d'hôte et d'exécution de Zipline afin que vous puissiez les mettre à niveau indépendamment.
Version tyrolienne hôte | Versions de tyrolienne d'exécution prises en charge |
---|---|
0.x | Exactement la même version 0.x que l'hôte. |
1.x | Toute version 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.
Ce projet était auparavant connu sous le nom de Duktape-Android et regroupait le moteur JavaScript Duktape pour Android. L'historique de Duktape est toujours présent dans ce dépôt, tout comme les balises de version. Les versions disponibles sont répertoriées sur Maven Central.