Diese Bibliothek optimiert die Verwendung von Kotlin/JS-Bibliotheken aus Kotlin/JVM- und Kotlin/Native-Programmen. Dadurch ist das Abrufen von Code genauso einfach wie das Abrufen von Daten:
Zipline funktioniert durch die Einbettung der QuickJS-JavaScript-Engine in Ihr Kotlin/JVM- oder Kotlin/Native-Programm. Es handelt sich um eine kleine und schnelle JavaScript-Engine, die sich gut zur Einbettung in Anwendungen eignet.
(Auf der Suche nach Duktape Android?)
Lassen Sie uns ein Quizspiel erstellen, das jeden Tag neue Fragen enthält, auch wenn unsere Benutzer ihre Apps nicht aktualisieren. Wir definieren unsere Schnittstelle in commonMain
, damit wir sie von Kotlin/JVM aus aufrufen und in Kotlin/JS implementieren können.
interface TriviaService : ZiplineService {
fun games (): List < TriviaGame >
fun answer ( questionId : String , answer : String ): AnswerResult
}
Als nächstes implementieren wir es in jsMain
:
class RealTriviaService : TriviaService {
// ...
}
Verbinden wir die in Kotlin/JS laufende Implementierung mit der in Kotlin/JVM laufenden Schnittstelle. In jsMain
definieren wir eine exportierte Funktion, um die Implementierung zu binden:
@JsExport
fun launchZipline () {
val zipline = Zipline .get()
zipline.bind< TriviaService >( " triviaService " , RealTriviaService ())
}
Jetzt können wir einen Entwicklungsserver starten, um unser JavaScript allen laufenden Anwendungen bereitzustellen, die es anfordern.
$ ./gradlew -p samples trivia:trivia-js:serveDevelopmentZipline --info --continuous
Beachten Sie, dass dieser Gradle niemals 100 % erreichen wird. Das wird erwartet; Wir möchten, dass der Entwicklungsserver eingeschaltet bleibt. Beachten Sie außerdem, dass das Flag --continuous
bei jeder Änderung des Codes eine Neukompilierung auslöst.
Sie können das bereitgestellte Anwendungsmanifest unter localhost:8080/manifest.zipline.json sehen. Es verweist auf alle Codemodule für die Anwendung.
In jvmMain
müssen wir ein Programm schreiben, das unseren Kotlin/JS-Code herunterlädt und aufruft. Wir verwenden ZiplineLoader
, der das Herunterladen, Zwischenspeichern und Laden von Code übernimmt. Wir erstellen einen Dispatcher
, auf dem Kotlin/JS ausgeführt wird. Dies muss ein Single-Threaded-Dispatcher sein, da jede Zipline-Instanz auf einen einzelnen Thread beschränkt sein muss.
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)
}
Jetzt erstellen wir das JVM-Programm und führen es aus, um alles zusammenzusetzen. Tun Sie dies in einem separaten Terminal vom Entwicklungsserver!
$ ./gradlew -p samples trivia:trivia-host:shadowJar
java -jar samples/trivia/trivia-host/build/libs/trivia-host-all.jar
Zipline erleichtert die gemeinsame Nutzung von Schnittstellen mit Kotlin/JS. Definieren Sie eine Schnittstelle in commonMain
, implementieren Sie sie in Kotlin/JS und rufen Sie sie von der Hostplattform aus auf. Oder machen Sie das Gegenteil: Implementieren Sie es auf der Host-Plattform und rufen Sie es über Kotlin/JS auf.
Überbrückte Schnittstellen müssen ZiplineService
erweitern, was eine einzelne close()
Methode definiert, um gehaltene Ressourcen freizugeben.
Standardmäßig werden Argumente und Rückgabewerte als Wert übergeben. Zipline verwendet kotlinx.serialization, um über die Grenze übergebene Werte zu kodieren und zu dekodieren.
Schnittstellentypen, die von ZiplineService
ausgehen, werden als Referenz übergeben: Der Empfänger kann Methoden für eine Live-Instanz aufrufen.
Schnittstellenfunktionen werden möglicherweise ausgesetzt. Intern implementiert Zipline setTimeout()
damit asynchroner Code so funktioniert, wie er es in Kotlin/JS soll.
Zipline unterstützt auch Flow<T>
als Parameter oder Rückgabetyp. Dies erleichtert den Aufbau reaktiver Systeme.
Ein potenzieller Engpass beim Einbetten von JavaScript besteht darin, darauf zu warten, dass die Engine den Eingabequellcode kompiliert. Zipline kompiliert JavaScript in effizienten QuickJS-Bytecode vor, um diese Leistungseinbußen zu vermeiden.
Ein weiterer Engpass besteht darin, darauf zu warten, dass Code heruntergeladen wird. Zipline begegnet diesem Problem mit der Unterstützung modularer Anwendungen. Jedes Eingabemodul (wie die Standard-, Serialisierungs- und Coroutinen-Bibliotheken von Kotlin) wird gleichzeitig heruntergeladen. Jedes heruntergeladene Modul wird zwischengespeichert. Module können auch in die Host-Anwendung eingebettet werden, um Downloads zu vermeiden, wenn das Netzwerk nicht erreichbar ist. Wenn sich Ihr Anwendungsmodul häufiger ändert als Ihre Bibliotheken, laden Benutzer nur das herunter, was geändert wurde.
Wenn in der QuickJS-Laufzeit Leistungsprobleme auftreten, enthält Zipline einen Sampling-Profiler. Damit können Sie eine Aufschlüsselung darüber erhalten, wie Ihre Anwendung ihre CPU-Zeit verbraucht.
Zipline implementiert console.log
indem es Nachrichten an die Hostplattform weiterleitet. Es verwendet android.util.Log
unter Android, java.util.logging
unter JVM und stdout
unter Kotlin/Native.
Zipline integriert Kotlin-Quellkarten in den QuickJS-Bytecode. Wenn Ihr Prozess abstürzt, gibt der Stacktrace .kt
Dateien und Zeilennummern aus. Auch wenn sich darunter JavaScript befindet, müssen Entwickler keine Schnittstelle zu .js
Dateien herstellen.
Nach der Verwendung einer überbrückten Schnittstelle muss diese geschlossen werden, damit das Peer-Objekt durch Garbage Collection erfasst werden kann. Dies ist schwer zu bewerkstelligen, weshalb Zipline Ideen von LeakCanary übernimmt und aggressiv erkennt, wenn ein close()
-Aufruf verpasst wird.
Zipline unterstützt die Signaturen EdDSA Ed25519 und ECDSA P-256 zur Authentifizierung heruntergeladener Bibliotheken.
Die Einrichtung ist unkompliziert. Generieren Sie ein EdDSA-Schlüsselpaar. Eine Aufgabe hierfür wird mit dem Zipline Gradle Plugin installiert.
$ ./gradlew :generateZiplineManifestKeyPairEd25519
...
---------------- ----------------------------------------------------------------
ALGORITHM: Ed25519
PUBLIC KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
PRIVATE KEY: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
---------------- ----------------------------------------------------------------
...
Legen Sie den privaten Schlüssel auf dem Build-Server ab und konfigurieren Sie ihn zum Signieren von Builds:
zipline {
signingKeys {
create( " key1 " ) {
privateKeyHex.set( .. .)
algorithmId.set(app.cash.zipline.loader. SignatureAlgorithmId . Ed25519 )
}
}
}
Geben Sie den öffentlichen Schlüssel in jede Hostanwendung ein und konfigurieren Sie ihn zur Überprüfung von Signaturen:
val manifestVerifier = ManifestVerifier . Builder ()
.addEd25519( " key1 " , .. .)
.build()
val loader = ZiplineLoader (
manifestVerifier = manifestVerifier,
.. .
)
Sowohl das Signieren als auch das Verifizieren akzeptieren mehrere Schlüssel, um die Schlüsselrotation zu unterstützen.
Zipline wurde entwickelt, um den Code Ihrer Organisation auszuführen, wann und wo Sie ihn möchten. Es bietet keine Sandbox oder Prozessisolierung und sollte nicht zum Ausführen von nicht vertrauenswürdigem Code verwendet werden.
Es ist wichtig zu bedenken, dass dieses Design implizites Vertrauen setzt auf:
Es schützt nicht vor Kompromissen jeglicher Art.
Außerdem bietet es noch keinen Mechanismus zum Verbot älterer (signierter) Versionen von ausführbarem Code, bei denen bekannte Probleme auftreten.
Es gibt ein paar Dinge, die Sie tun können, um sicherzustellen, dass Hot-Reload so schnell wie möglich ausgeführt wird:
kotlin.incremental.js.ir=true
hinzu, um die inkrementelle Kompilierung von Kotlin/JS zu aktivieren.org.gradle.unsafe.configuration-cache=true
hinzu, um den Gradle-Konfigurationscache zu aktivieren.tasks.withType(DukatTask::class) { enabled = false }
hinzu, um die Dukat-Aufgabe zu deaktivieren, wenn Sie keine TypeScript-Typdeklarationen verwenden.Zipline funktioniert auf Android 4.3+ (API-Level 18+), Java 8+ und Kotlin/Native.
Zipline verwendet in seiner Implementierung instabile APIs und reagiert empfindlich auf Versionsaktualisierungen dieser Komponenten.
Komponente | Unterstützte Version | Notizen |
---|---|---|
Kotlin-Compiler | 2.0.0 | Kotlin-Compiler-Plugins verfügen noch nicht über eine stabile API. |
Kotlin-Serialisierung | 1.6.3 | Für decodeFromDynamic() , encodeToDynamic() und ContextualSerializer . |
Kotlin-Coroutinen | 1.8.1 | Für transformLatest() , Deferred.getCompleted() und CoroutineStart.ATOMIC . |
Wir beabsichtigen, stabile APIs zu verwenden, sobald diese verfügbar sind.
Wir beabsichtigen, die Interoperabilität der Zipline-Host- und Runtime-Releases sicherzustellen, sodass Sie jedes unabhängig voneinander aktualisieren können.
Host-Zipline-Version | Unterstützte Laufzeit-Zipline-Versionen |
---|---|
0.x | Genau die gleiche 0.x-Version wie der Host. |
1.x | Jede 1.x-Version. |
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.
Dieses Projekt war zuvor als Duktape-Android bekannt und bündelte die Duktape-JavaScript-Engine für Android. Der Duktape-Verlauf ist in diesem Repo noch vorhanden, ebenso wie die Release-Tags. Verfügbare Versionen sind auf Maven Central aufgeführt.