Esta biblioteca simplifica o uso de bibliotecas Kotlin/JS de programas Kotlin/JVM e Kotlin/Native. Isso torna a busca de código tão fácil quanto a busca de dados:
Zipline funciona incorporando o mecanismo JavaScript QuickJS em seu programa Kotlin/JVM ou Kotlin/Native. É um mecanismo JavaScript pequeno e rápido, adequado para incorporação em aplicativos.
(Procurando por Duktape Android?)
Vamos fazer um jogo de perguntas e respostas que traz novas perguntas todos os dias, mesmo que nossos usuários não atualizem seus aplicativos. Definimos nossa interface em commonMain
para que possamos chamá-la de Kotlin/JVM e implementá-la em Kotlin/JS.
interface TriviaService : ZiplineService {
fun games (): List < TriviaGame >
fun answer ( questionId : String , answer : String ): AnswerResult
}
Em seguida, implementamos isso em jsMain
:
class RealTriviaService : TriviaService {
// ...
}
Vamos conectar a implementação em execução em Kotlin/JS à interface em execução em Kotlin/JVM. Em jsMain
definimos uma função exportada para vincular a implementação:
@JsExport
fun launchZipline () {
val zipline = Zipline .get()
zipline.bind< TriviaService >( " triviaService " , RealTriviaService ())
}
Agora podemos iniciar um servidor de desenvolvimento para servir nosso JavaScript a qualquer aplicativo em execução que o solicite.
$ ./gradlew -p samples trivia:trivia-js:serveDevelopmentZipline --info --continuous
Observe que este Gradle nunca chegará a 100%. Isso é esperado; queremos que o servidor de desenvolvimento permaneça ligado. Observe também que o sinalizador --continuous
irá acionar uma recompilação sempre que o código for alterado.
Você pode ver o manifesto do aplicativo servido em localhost:8080/manifest.zipline.json. Ele faz referência a todos os módulos de código do aplicativo.
No jvmMain
precisamos escrever um programa que baixe nosso código Kotlin/JS e o chame. Usamos ZiplineLoader
, que lida com download, armazenamento em cache e carregamento de código. Criamos um Dispatcher
para executar Kotlin/JS. Este deve ser um despachante de thread único, pois cada instância do Zipline deve ser confinada a um único 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)
}
Agora construímos e executamos o programa JVM para juntar tudo. Faça isso em um terminal separado do servidor de desenvolvimento!
$ ./gradlew -p samples trivia:trivia-host:shadowJar
java -jar samples/trivia/trivia-host/build/libs/trivia-host-all.jar
Zipline facilita o compartilhamento de interfaces com Kotlin/JS. Defina uma interface em commonMain
, implemente-a em Kotlin/JS e chame-a da plataforma host. Ou faça o oposto: implemente-o na plataforma host e chame-o de Kotlin/JS.
As interfaces em ponte devem estender ZiplineService
, que define um único método close()
para liberar recursos retidos.
Por padrão, os argumentos e os valores de retorno são transmitidos por valor. Zipline usa kotlinx.serialization para codificar e decodificar valores transmitidos através da fronteira.
Os tipos de interface que se estendem de ZiplineService
são passados por referência: o receptor pode chamar métodos em uma instância ativa.
As funções da interface podem estar suspensas. Internamente, a Zipline implementa setTimeout()
para fazer o código assíncrono funcionar como deveria em Kotlin/JS.
Zipline também oferece suporte Flow<T>
como parâmetro ou tipo de retorno. Isso facilita a construção de sistemas reativos.
Um gargalo potencial na incorporação de JavaScript é aguardar que o mecanismo compile o código-fonte de entrada. Zipline pré-compila JavaScript em bytecode QuickJS eficiente para eliminar essa penalidade de desempenho.
Outro gargalo é aguardar o download do código. A Zipline resolve isso com suporte para aplicações modulares. Cada módulo de entrada (como as bibliotecas padrão, de serialização e de corrotinas do Kotlin) é baixado simultaneamente. Cada módulo baixado é armazenado em cache. Os módulos também podem ser incorporados ao aplicativo host para evitar downloads se a rede estiver inacessível. Se o módulo do seu aplicativo for alterado com mais frequência do que as suas bibliotecas, os usuários baixarão apenas o que foi alterado.
Se você tiver problemas de desempenho no tempo de execução do QuickJS, o Zipline inclui um criador de perfil de amostragem. Você pode usar isso para obter um detalhamento de como seu aplicativo gasta o tempo de CPU.
Zipline implementa console.log
encaminhando mensagens para a plataforma host. Ele usa android.util.Log
no Android, java.util.logging
na JVM e stdout
no Kotlin/Native.
Zipline integra mapas de origem Kotlin em bytecode QuickJS. Se o seu processo travar, o stacktrace imprimirá arquivos .kt
e números de linha. Mesmo que haja JavaScript por baixo, os desenvolvedores não precisam fazer interface com arquivos .js
.
Depois de usar uma interface em ponte, ela deve ser fechada para que o objeto peer possa ser coletado como lixo. É difícil acertar, então o Zipline pega emprestadas ideias do LeakCanary e detecta agressivamente quando uma chamada close()
é perdida.
Zipline suporta assinaturas EdDSA Ed25519 e ECDSA P-256 para autenticar bibliotecas baixadas.
A configuração é simples. Gere um par de chaves EdDSA. Uma tarefa para isso é instalada com o plugin Zipline Gradle.
$ ./gradlew :generateZiplineManifestKeyPairEd25519
...
---------------- ----------------------------------------------------------------
ALGORITHM: Ed25519
PUBLIC KEY: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
PRIVATE KEY: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
---------------- ----------------------------------------------------------------
...
Coloque a chave privada no servidor de build e configure-a para assinar builds:
zipline {
signingKeys {
create( " key1 " ) {
privateKeyHex.set( .. .)
algorithmId.set(app.cash.zipline.loader. SignatureAlgorithmId . Ed25519 )
}
}
}
Coloque a chave pública em cada aplicativo host e configure-a para verificar assinaturas:
val manifestVerifier = ManifestVerifier . Builder ()
.addEd25519( " key1 " , .. .)
.build()
val loader = ZiplineLoader (
manifestVerifier = manifestVerifier,
.. .
)
Tanto a assinatura quanto a verificação aceitam múltiplas chaves para suportar a rotação de chaves.
Zipline foi projetado para executar o código da sua organização quando e onde você quiser. Ele não oferece sandbox ou isolamento de processo e não deve ser usado para executar código não confiável.
É essencial ter em mente que este design coloca confiança implícita em:
Não protege contra qualquer tipo de comprometimento dos itens acima.
Além disso, ainda não fornece um mecanismo para proibir versões mais antigas (assinadas) de código executável que apresentam problemas conhecidos.
Existem algumas coisas que você pode fazer para garantir que o hot-reload esteja funcionando o mais rápido possível:
kotlin.incremental.js.ir=true
para ativar a compilação incremental Kotlin/JS.org.gradle.unsafe.configuration-cache=true
para ativar o cache de configuração do Gradle.tasks.withType(DukatTask::class) { enabled = false }
para desativar a tarefa Dukat se você não estiver usando declarações de tipo TypeScript.Zipline funciona em Android 4.3+ (API de nível 18+), Java 8+ e Kotlin/Native.
Zipline usa APIs instáveis em sua implementação e é sensível a atualizações de versão desses componentes.
Componente | Versão suportada | Notas |
---|---|---|
Compilador Kotlin | 2.0.0 | Os plug-ins do compilador Kotlin ainda não possuem uma API estável. |
Serialização Kotlin | 1.6.3 | Para decodeFromDynamic() , encodeToDynamic() e ContextualSerializer . |
Corrotinas Kotlin | 1.8.1 | Para transformLatest() , Deferred.getCompleted() e CoroutineStart.ATOMIC . |
Pretendemos usar APIs estáveis assim que estiverem disponíveis.
Pretendemos manter o host Zipline e as versões de tempo de execução interoperáveis para que você possa atualizar cada um de forma independente.
Versão Zipline do Host | Versões Zipline em tempo de execução suportadas |
---|---|
0.x | Exatamente a mesma versão 0.x do host. |
1.x | Qualquer versão 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 projeto era anteriormente conhecido como Duktape-Android e empacotava o mecanismo JavaScript Duktape para Android. A história do Duktape ainda está presente neste repositório, assim como as tags de lançamento. As versões disponíveis estão listadas na central do Maven.