이 라이브러리는 Kotlin/JVM 및 Kotlin/Native 프로그램의 Kotlin/JS 라이브러리 사용을 간소화합니다. 데이터를 가져오는 것만큼 쉽게 코드를 가져올 수 있습니다.
Zipline은 Kotlin/JVM 또는 Kotlin/Native 프로그램에 QuickJS JavaScript 엔진을 삽입하여 작동합니다. 애플리케이션에 임베드하는 데 적합한 작고 빠른 JavaScript 엔진입니다.
(Duktape Android를 찾고 계시나요?)
사용자가 앱을 업데이트하지 않더라도 매일 새로운 질문이 있는 퀴즈 게임을 만들어 보겠습니다. Kotlin/JVM에서 호출하고 Kotlin/JS에서 구현할 수 있도록 commonMain
에서 인터페이스를 정의합니다.
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
사용합니다. Kotlin/JS를 실행할 Dispatcher
만듭니다. 각 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에서 호출합니다.
브리지 인터페이스는 보유 리소스를 해제하기 위한 단일 close()
메서드를 정의하는 ZiplineService
확장해야 합니다.
기본적으로 인수와 반환 값은 값별로 전달됩니다. Zipline은 kotlinx.serialization을 사용하여 경계를 넘어 전달되는 값을 인코딩하고 디코딩합니다.
ZiplineService
에서 확장되는 인터페이스 유형은 참조를 통해 전달됩니다. 수신자는 라이브 인스턴스에서 메서드를 호출할 수 있습니다.
인터페이스 기능이 일시 중지될 수 있습니다. 내부적으로 Zipline은 setTimeout()
구현하여 비동기 코드가 Kotlin/JS에서 예상되는 대로 작동하도록 합니다.
Zipline은 매개변수 또는 반환 유형으로 Flow<T>
도 지원합니다. 이를 통해 반응형 시스템을 쉽게 구축할 수 있습니다.
JavaScript 삽입 시 발생할 수 있는 병목 현상 중 하나는 엔진이 입력 소스 코드를 컴파일할 때까지 기다리는 것입니다. Zipline은 JavaScript를 효율적인 QuickJS 바이트코드로 사전 컴파일하여 이러한 성능 저하를 제거합니다.
또 다른 병목 현상이 코드 다운로드를 기다리고 있습니다. Zipline은 모듈형 애플리케이션을 지원하여 이 문제를 해결합니다. 각 입력 모듈(예: Kotlin의 표준, 직렬화, 코루틴 라이브러리)은 동시에 다운로드됩니다. 다운로드된 각 모듈은 캐시됩니다. 네트워크에 연결할 수 없는 경우 다운로드를 방지하기 위해 모듈을 호스트 애플리케이션에 내장할 수도 있습니다. 애플리케이션 모듈이 라이브러리보다 더 자주 변경되는 경우 사용자는 변경된 내용만 다운로드합니다.
QuickJS 런타임에서 성능 문제가 발생하는 경우 Zipline에는 샘플링 프로파일러가 포함되어 있습니다. 이를 사용하여 애플리케이션이 CPU 시간을 어떻게 소비하는지 분석할 수 있습니다.
Zipline은 호스트 플랫폼에 메시지를 전달하여 console.log
구현합니다. Android에서는 android.util.Log
, JVM에서는 java.util.logging
, Kotlin/Native에서는 stdout
사용합니다.
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 작업을 끄십시오.Zipline은 Android 4.3 이상(API 레벨 18 이상), Java 8 이상, Kotlin/Native에서 작동합니다.
Zipline은 구현 시 불안정한 API를 사용하며 이러한 구성 요소의 버전 업데이트에 민감합니다.
요소 | 지원되는 버전 | 메모 |
---|---|---|
코틀린 컴파일러 | 2.0.0 | Kotlin 컴파일러 플러그인에는 아직 안정적인 API가 없습니다. |
Kotlin 직렬화 | 1.6.3 | decodeFromDynamic() , encodeToDynamic() 및 ContextualSerializer 의 경우. |
코틀린 코루틴 | 1.8.1 | transformLatest() , Deferred.getCompleted() 및 CoroutineStart.ATOMIC 의 경우. |
우리는 안정적인 API가 출시되는 대로 이를 사용할 계획입니다.
우리는 Zipline 호스트와 런타임 릴리스를 상호 운용 가능하게 유지하여 각각을 독립적으로 업그레이드할 수 있도록 할 계획입니다.
호스트 집라인 버전 | 지원되는 런타임 Zipline 버전 |
---|---|
0.x | 호스트와 정확히 동일한 0.x 버전입니다. |
1.x | 모든 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로 알려졌으며 Android용 Duktape JavaScript 엔진을 패키징했습니다. Duktape 기록은 릴리스 태그와 마찬가지로 이 저장소에 여전히 존재합니다. 사용 가능한 버전은 Maven Central에 나열되어 있습니다.