基於KotlinMultiPlatform構建跨平臺項目

前言

當下市場上的跨端解決方案, 無論使用的是React Native, Flutter 仍是Weex, 常見的項目組成是, 業務UI界面由上述框架解決, 而涉及不管是性能問題, 仍是平臺通用共享邏輯問題, 咱們更側重於原生開發, 而這一塊咱們必不可免須要至少兩個原生開發同窗經過溝通和開發統一Native層的功能邏輯, 而針對於這些原生通用代碼, 現有常見的解決方案仍是主要經過傳統的C/C++來解決.html

而如今, JetBrains的Kotlin/Native爲咱們提供了另一個解決方案.前端

什麼是Kotlin/Native

Kotlin/Native 是一種將 Kotlin 代碼編譯爲無需虛擬機就可運行的原生二進制文件的技術。 它是一個基於 LLVM 的 Kotlin 編譯器後端以及 Kotlin 標準庫的原生實現。react

LLVM是一種編譯器基礎結構,它基於具備可從新定位性的三階段設計的概念。簡單來講,這意味着能夠爲具備LLVM後端編譯器的任何目標編譯具備前端LLVM編譯器的任何語言。(Swift能夠編譯Android工程也是基於LLVM實現)linux

LLVM

Kotlin/Native 支持如下平臺:android

  • iOS(arm3二、 arm6四、 模擬器 x86_64)
  • MacOS(x86_64)
  • Android(arm3二、arm64)
  • Windows(mingw x86_6四、x86)
  • Linux(x86_6四、 arm3二、 MIPS、 MIPS 小端次序、樹莓派)
  • WebAssembly(wasm32)

Kotlin支持兩種與Native的互操做行爲方式:ios

  1. 經過Kotlin/Native內包含的cinterop tool經過自建立def文件解析C頭文件, 快速生成與Kotlin須要交互的全部內容(包括類型, 函數, 常量)
  2. 直接使用現有庫的互操做, 其中包括
    1. 靜態或動態 C 語言庫
    2. C 語言、 Swift 以及 Objective-C 框架

當前POSIX、 gzip、 OpenGL、 Metal、 Foundation 以及許多其餘流行庫與 Apple 框架都已預先導入並做爲 Kotlin/Native 庫包含在編譯器包中git

Kotlin MultiPlatform(KMP)

在Kotlin/Native(Beta)1.3.30版本開始, Kotlin/Native是做爲KMP中的目標平臺之一, 當前咱們能夠基於KMP構建咱們的跨端共享代碼項目github

Kotlin MultiPlatform

在KMP DSL中咱們有幾個基礎概念須要瞭解objective-c

  1. Targetsql

    它表示KMP工程的產出變體, 根據配置可生成對應的android庫, ios的framework, jvm應用等等..

  2. Present

    用來定義對應Target的預配置, 譬如可經過fromPreset(<PRESET_KIND>, <TARGET_NAME>)設置, PRESET_KIND須要使用當前存在的值, 當前目標預設值以下:

    • androidNativeArm32 and androidNativeArm64 for Android NDK;
    • android for Android
    • 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

KMP工程目錄結構

expect / actual

當兩端實現存在各自差別化代碼時, 能夠經過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進行分別實現.

ios上的互操做

在執行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];
  }];
  

}
複製代碼

這樣就實現了雙端底層的代碼共享.

其餘

多平臺三方庫的支持

  1. 序列化 Kotlin Serialization
  2. IO Kotlin io
  3. 網絡 Ktor
  4. 數據庫 sqldelight

學習成本

  1. Kotlin語言學習
  2. 雙端必定開發基礎
  3. 多平臺庫的學習

文章參考

  1. kotlin官方社區
  2. Swift for Android
  3. Interop
  4. Kotlin Native Github
相關文章
相關標籤/搜索