當下市場上的跨端解決方案, 無論使用的是React Native
, Flutter
仍是Weex
, 常見的項目組成是, 業務UI界面由上述框架解決, 而涉及不管是性能問題, 仍是平臺通用共享邏輯問題, 咱們更側重於原生開發, 而這一塊咱們必不可免須要至少兩個原生開發同窗經過溝通和開發統一Native層的功能邏輯, 而針對於這些原生通用代碼, 現有常見的解決方案仍是主要經過傳統的C/C++來解決.html
而如今, JetBrains的Kotlin/Native
爲咱們提供了另一個解決方案.前端
Kotlin/Native 是一種將 Kotlin 代碼編譯爲無需虛擬機就可運行的原生二進制文件的技術。 它是一個基於 LLVM 的 Kotlin 編譯器後端以及 Kotlin 標準庫的原生實現。react
LLVM
是一種編譯器基礎結構,它基於具備可從新定位性的三階段設計的概念。簡單來講,這意味着能夠爲具備LLVM後端編譯器的任何目標編譯具備前端LLVM編譯器的任何語言。(Swift能夠編譯Android工程也是基於LLVM實現)linux
Kotlin/Native 支持如下平臺:android
Kotlin支持兩種與Native的互操做行爲方式:ios
cinterop
tool經過自建立def文件解析C頭文件, 快速生成與Kotlin須要交互的全部內容(包括類型, 函數, 常量)當前POSIX、 gzip、 OpenGL、 Metal、 Foundation 以及許多其餘流行庫與 Apple 框架都已預先導入並做爲 Kotlin/Native 庫包含在編譯器包中git
在Kotlin/Native(Beta)1.3.30版本開始, Kotlin/Native是做爲KMP
中的目標平臺之一, 當前咱們能夠基於KMP
構建咱們的跨端共享代碼項目github
在KMP DSL中咱們有幾個基礎概念須要瞭解objective-c
Targetsql
它表示KMP工程的產出變體, 根據配置可生成對應的android庫, ios的framework, jvm應用等等..
Present
用來定義對應Target的預配置, 譬如可經過fromPreset(<PRESET_KIND>, <TARGET_NAME>)
設置, PRESET_KIND
須要使用當前存在的值, 當前目標預設值以下:
androidNativeArm32
and androidNativeArm64
for Android NDK;iosArm32
, iosArm64
, iosX64
for iOS;linuxArm32Hfp
, linuxMips32
, linuxMipsel32
, linuxX64
for Linux;macosX64
for MacOS;mingwX64
for Windows;wasm32
for WebAssembly.同時, TARGET_NAMAE值以下:
jvm
for Kotlin/JVM.js
for Kotlin/JS;android
for Android applications and libraries. (AGP插件的引用須要在targets生成以前)咱們可使用Android Studio或者IntelliJ IDEA進行開發, 對應IDE須要安裝Kotlin插件1.3.21及以上版本. 安裝XCode(跑ios工程).
建立Kotlin-MultiPlatform工程, 咱們須要建立一個基於Gradle構建工程, 這個原生Android工程就能夠知足, 而後咱們經過引用kotlin-multiplatform`插件進行部署工做.
經過kotlin.targets設置目標平臺, 下方代碼是設置ios平臺和android平臺, 固然也能夠設置jvm和js端.
kotlin {
targets {
final def iOSTarget = System.getenv('SDK_NAME')?.startsWith("iphoneos") \
? presets.iosArm64 : presets.iosX64
fromPreset(iOSTarget, 'ios') {
binaries {
framework(project.name)
}
}
fromPreset(presets.android, 'android')
}
}
複製代碼
不一樣targets依賴的三方庫可能不一樣, 能夠經過kotlin.sourceSets設置.
kotlin{
sourceSets {
commonMain {
dependencies{
api 'org.jetbrains.kotlin:kotlin-stdlib-common'
implementation "io.ktor:ktor-client-core:$ktor_version"
implementation "io.ktor:ktor-client-json:$ktor_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:$coroutines_version"
// implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$serializer_version"
}
}
androidMain {
dependencies {
api 'org.jetbrains.kotlin:kotlin-stdlib'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
// implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$serializer_version"
implementation "io.ktor:ktor-client-android:$ktor_version"
}
}
iosMain{
dependencies{
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:$coroutines_version"
implementation "io.ktor:ktor-client-ios:$ktor_version"
implementation "io.ktor:ktor-client-core-native:$ktor_version"
implementation "io.ktor:ktor-client-json-native:$ktor_version"
// implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$serializer_version"
}
}
}
}
複製代碼
同時不一樣源集工程目錄構建以下圖, 須要注意的是, 源集的命名須要遵循KMP的強加的特定命名, 它是根據target + compilation來命名的, 若是咱們這裏須要一個ios特性的測試模塊, 那麼新增的源集命名應該爲iosTest
當兩端實現存在各自差別化代碼時, 能夠經過expect/actual
關鍵字進行表示, expect
須要在common中進行聲明, 它能夠被理解爲接口的聲明, 不過它不只能修飾類, 也能夠修飾單獨的對象.actual
修飾對應的實現.
以下方代碼, 線程調度在不一樣平臺上的實現是不一樣的, 因此咱們須要指定對應Dispatcher
// in commonMain Module Dispatcher.kt
internal expect val applicationDispatcher: CoroutineDispatcher
複製代碼
// in androidMain Module Dispatcher.kt
internal actual val applicationDispatcher: CoroutineDispatcher = Dispatchers.Default
複製代碼
// in iosMain Module Dispatcher.kt
internal actual val applicationDispatcher: CoroutineDispatcher = NsQueueDispatcher(dispatch_get_main_queue())
internal class NsQueueDispatcher(private val dispatchQueue: dispatch_queue_t) : CoroutineDispatcher() {
override fun dispatch(context: CoroutineContext, block: Runnable) {
dispatch_async(dispatchQueue) {
block.run()
}
}
}
複製代碼
一樣, 像基於各端自身特性的API的兼容, 咱們基本都須要經過expect/actual
進行分別實現.
在執行build後, 構建目錄下會生成供ios使用的靜態庫, 而後經過XCode新建工程, 配置引入對應的framework, 並在構建流程上添加對應的腳本配置, 使其編譯的時候能夠自動經過gradle編譯kt庫更新framework.(因爲咱們的Demo工程是基於RN框架的, 因此在經過react-native init
後咱們在對應生成的ios工程下直接配置便可)
咱們經過引用component, 能夠直接調到kt的編譯生成的二進制代碼.
RCT_EXPORT_METHOD (requestWith:(NSString *)url
params:(NSString *)params
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
ComponentBaseHttpClient *o = [ComponentBaseHttpClient new];
[o requestRequestUrl:url block:^ComponentKotlinUnit * _Nonnull(ComponentKtor_client_coreHttpRequestBuilder * _Nonnull httpBuilder) {
return [ComponentKotlinUnit unit];
} success:^ComponentKotlinUnit * _Nonnull(NSString * _Nonnull responce) {
resolve(responce);
return [ComponentKotlinUnit unit];
} failure:^ComponentKotlinUnit * _Nonnull(ComponentKotlinThrowable * _Nullable e) {
if(e != nil){
reject(@"1", e.message, nil);
}
return [ComponentKotlinUnit unit];
}];
}
複製代碼
這樣就實現了雙端底層的代碼共享.