攜程機票Skip原生跨端框架探究
本文介紹Skip這一款新興的高性能原生跨端開發框架,其通過將Swift和SwiftUI代碼智能轉換為Kotlin和Jetpack Compose代碼,實現Android與iOS雙端的高效開發。文章詳細解析Skip的架構設計、工具鏈支持及代碼轉換策略,并通過實際案例展示其開發流程與技術優勢。同時對比Skip與Flutter、React Native等主流框架,突出其在性能、代碼共享和開發體驗上的卓越表現。
一、Skip工具原理
二、Skip的使用方法
三、與其他跨端技術的對比
四、Skip Demo工程結構分析
五、Skip內部模塊
六、使用Skip的注意事項
七、總結
在移動應用開發領域,跨端開發框架一直備受關注。隨著Flutter、React Native和Kotlin Multiplatform等方案的普及,開發者能夠在不同平臺上共享代碼,從而提升迭代效率。然而,每種跨端框架都有其優缺點,在開發體驗、動態更新、渲染性能和社區生態等方面表現各異。
Skip是由Glimpse I/O, Inc.于2023年推出的一款支持Swift和Kotlin的高性能原生跨端開發框架。該框架旨在顯著縮短Android和iOS雙端的開發時間,同時降低維護成本。憑借其卓越的技術特性,Skip在跨端開發領域展現了顯著優勢,包括:
- 高性能原生體驗:通過直接編譯為各平臺的原生代碼,最大限度地減少性能損耗,確保應用運行流暢。
- 統一的開發體驗:支持開發者使用單一代碼庫構建多端應用,大幅提升開發效率并降低復雜性。
- 模塊化架構:采用靈活的模塊化設計,允許開發者按需引入功能模塊,優化資源占用和項目結構。
盡管Skip技術理念優勢顯著,但目前仍無法完全實現將Swift和SwiftUI的功能無縫復刻到Kotlin和Jetpack Compose中。期待后續迭代更新,最終實現跨平臺等價轉換能力。
本文將介紹Skip這一跨端框架,深入探討其原理、核心庫,并與現有的原生跨端技術(如Kotlin Multiplatform和Compose Multiplatform)進行詳細對比。
一、Skip工具原理
Skip的架構圖展示了其工作流程:
Skip的核心設計理念是“原生優先”,能夠直接利用原生平臺的UI組件和系統能力。
與Flutter和React Native不同,Skip不依賴于自繪引擎或JavaScript橋接,也不同于Kotlin Multiplatform將Kotlin代碼編譯成各個平臺的目標代碼。Skip的工作原理基于現代編程語言的相似性,通過將Swift和SwiftUI的代碼轉換為Kotlin和Jetpack Compose的代碼,實現跨平臺代碼共享。
主要特點包括:
代碼共享:Skip允許開發者使用Kotlin(針對Android)和Swift(針對iOS)編寫共享的業務邏輯代碼。通過一種輕量級的抽象層,Skip將這些代碼轉換為原生平臺的實現,從而避免了跨平臺框架常見的性能損耗。
原生UI組件:Skip不引入額外的UI框架,而是直接使用Android的Jetpack Compose和iOS的SwiftUI。這意味著開發者可以享受到原生UI的高性能和流暢體驗,同時減少學習成本。
狀態管理:Skip提供了一套輕量級的狀態管理機制,支持在共享代碼中定義和管理應用的狀態,并通過高效的同步機制確保狀態變化能夠實時反映到原生UI層。
工具鏈支持:Skip提供了完整的工具鏈支持,包括代碼生成、調試工具和構建腳本,幫助開發者快速上手并優化開發流程。
實際運行效果:不僅邏輯部分可共享,UI部分也可以做到一碼雙端。
二、Skip的使用方法
2.1 環境搭建
Skip的環境初始化配置非常簡單:
1)安裝Kotlin和Swift開發環境
2)通過Homebrew安裝Skip CLI工具:
brew install skip-dev/tap/skip
3)初始化一個新的Skip項目:
skip init MyApp
2.2 編寫共享代碼
在Skip項目中,共享代碼位于shared目錄下。開發者可以使用Kotlin或Swift編寫業務邏輯,例如網絡請求、數據存儲等。
class MyApp {
fun greet(): String {
return "Hello, Skip!"
}
}
2.3 實現原生UI
在Android和iOS項目中,分別使用Jetpack Compose和SwiftUI實現UI層,并調用共享邏輯代碼。
Swift編寫共享代碼
public struct RootView: View {
public init() {}
public var body: some View {
ContentView()
.task {
logger.log("Welcome to Skip on \(androidSDK != nil ? "Android" : "Darwin")!")
logger.warning("Skip app logs are viewable in the Xcode console for iOS; Android logs can be viewed in Studio or using adb logcat")
}
}
}
public struct ContentView: View {
@AppStorage("tab") var tab = ContentTab.welcome11
@State var viewModel = ViewModel()
@State var appearance = ""
public init() {}
public var body: some View {
TabView(selection: $tab) {
NavigationStack {
WelcomeView()
}
.tabItem { Label("Welcome1234885z211", systemImage: "heart.fill") }
.tag(ContentTab.welcome11)
NavigationStack {
ItemListView()
.navigationTitle(Text("\(viewModel.items.count) Items"))
}
.tabItem { Label("Home2", systemImage: "house.fill") }
.tag(ContentTab.home22)
NavigationStack {
SettingsView(appearance: $appearance)
.navigationTitle("Settings")
}
.tabItem { Label("Settings3", systemImage: "gearshape.fill") }
.tag(ContentTab.settings33)
}
.environment(viewModel)
.preferredColorScheme(appearance == "dark" ? .dark : appearance == "light" ? .light : nil)
}
}
iOS接入
#if !SKIP
public protocol SwiftToAndroidApp: App {}
public extension SwiftToAndroidApp {
var body: some Scene {
WindowGroup {
RootView()
}
}
}
#endif
Android接入
open class MainActivity: AppCompatActivity {
constructor() {}
override fun onCreate(savedInstanceState: android.os.Bundle?) {
super.onCreate(savedInstanceState)
logger.info("starting activity")
UIApplication.launch(this)
enableEdgeToEdge()
setContent {
val saveableStateHolder = rememberSaveableStateHolder()
saveableStateHolder.SaveableStateProvider(true) {
PresentationRootView(ComposeContext())
SideEffect { saveableStateHolder.removeState(true) }
}
}
}
}
@Composable
internal fun PresentationRootView(context: ComposeContext) {
val colorScheme = if (isSystemInDarkTheme()) ColorScheme.dark else ColorScheme.light
PresentationRoot(defaultColorScheme = colorScheme, context = context) { ctx ->
val contentContext = ctx.content()
Box(modifier = ctx.modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
RootView().Compose(context = contentContext)
}
}
}
2.4 構建和運行
使用Skip CLI工具構建并運行項目:
skip build
skip run
2.5 調試工具
Skip基于Swift語言生成Kotlin和Jetpack Compose的代碼產物,因此調試沿用iOS和Android的開發工具,即Xcode和Android Studio。這相較于其他跨端框架對移動端原生開發者非常友好,尤其對于KMP的開發者。
三、與其他跨端技術的對比
3.1 技術對比
Skip與Flutter、React Native、Kotlin Multiplatform和Compose Multiplatform的詳細對比:
3.2 Binary Size對比
以Release版本驗證數據對比,Skip的生成產物體積最小,非常適合對安裝包大小敏感的應用場景。
3.3 運行性能Benchmark對比
Skip在UI渲染性能、啟動時間和內存占用方面表現出色,相較于其他跨端框架具有顯著優勢。
官方對比數據,對使用Skip進行iOS和Android雙平臺開發與其他一些主流跨平臺應用程序構建方案進行比較。
各種主流跨平臺開發框架的底層技術方案:
四、Skip Demo工程結構分析
Skip的工程大致可分為以下四大模塊:
- Shared Module:包含共享的業務邏輯代碼,使用Kotlin或Swift編寫。
- Android Module:使用Jetpack Compose實現UI層,并調用共享代碼。
- iOS Module:使用SwiftUI實現UI層,并調用共享代碼。
- Skip Toolchain:提供代碼生成、構建和調試支持。
SkipDemo實際工程結構如圖:
Xcode對SkipDemo工程編譯生成的Android源碼和依賴都在skipstone文件夾下,開發人員編寫的代碼轉譯產物在SkipDemo中,其依賴項和其同級,此代碼可以單獨用AS直接打開運行。具體如下:
針對SkipDemo,也可直接通過Xcode來運行,這是因為在初始化SkipDemo項目時,工程配置中已經包含了構建和運行Android的腳本。
生成的Android工程分析
Android入口外殼app工程,在Activity的onCreate中,通過Compose的標準方式setContent來構建視圖。關鍵點在于,SwiftUI使用對象作為界面元素,而Jetpack Compose需要將界面元素轉換為@Composable函數。
實現策略詳見:Skip UI Implementation Strategy
在轉換代碼中,SwiftUI的每個View都有一個body,這里返回的是SwiftUI的頁面元素,轉換之后需要提供的是可以調用的@Composable函數,這里包裝了一層ComposeBuilder用于執行Compose方法返回@Composable函數的結果用于展示。
SwiftUI轉Compose的示例:
SwiftUI
struct V: View {
let isHello: Bool
var body: some View {
if isHello {
Text("Hello!")
} else {
Text("Goodbye!")
}
}
}
Compose
internal class V: View {
internal val isHello: Boolean
override fun body(): View {
return ComposeBuilder { composectx: ComposeContext ->
if (isHello) {
Text(LocalizedStringKey(stringLiteral = "Hello!")).Compose(composectx)
} else {
Text(LocalizedStringKey(stringLiteral = "Goodbye!")).Compose(composectx)
}
ComposeResult.ok
}
}
constructor(isHello: Boolean) {
this.isHello = isHello
}
}
SkipUI的工作原理:
以skip-ui中EmptyView的分析:
轉換器通過SKIP的宏定義,通過判斷此宏定義來分離Kotlin還是Swift。SKIP包圍的是Kotlin的代碼,非SKIP的就是專屬Swift的代碼,同時有些關鍵字的替換,如Swift的構造方法init在轉換后的代碼中就是constructor等。具體如下:
五、Skip內部模塊
Skip的核心框架涵蓋了從基礎功能(如狀態管理、UI渲染)到高級特性(如藍牙支持、Firebase集成)的多個模塊,為開發者提供了全面的跨平臺開發支持。
核心框架
- skip-unit
- skip-lib
- skip-foundation
- skip-model
- skip-ui
- skip-fuse
- skip-fuse-ui
額外框架
- skip-bluetooth
- skip-device
- skip-ffi
- skip-firebase
- skip-keychain
- skip-kit
- skip-motion
- skip-script
- skip-sql
- skip-web
- skip-zip
- skip-bridge
詳見Skip模塊
六、使用Skip的注意事項
平臺差異處理:由于Skip直接使用原生UI組件,開發者需要處理Android和iOS平臺的差異,例如導航欄、手勢等。
狀態管理:Skip的狀態管理機制較為簡潔,但在復雜場景下可能需要引入額外的狀態管理庫。
生態系統:Skip目前仍處于初期階段,生態系統和社區支持相對較弱,開發者可能需要自行解決一些問題。
調試工具:Skip的調試工具目前主要依賴Android Studio和Xcode,開發者可以利用這些成熟的工具進行調試。
七、總結
Skip作為一款新興的跨端開發框架和工具,以其原生優先的設計理念和簡潔的開發體驗,為開發者提供了一種全新的選擇。盡管其在生態系統和社區支持方面仍有待完善,但其在高性能和原生體驗方面的優勢,已經吸引了越來越多的開發者關注。
核心框架涵蓋了從基礎功能到高級特性的多個模塊,為開發者提供了全面的跨平臺開發支持。通過合理使用這些框架,開發者可以高效地實現跨平臺應用的開發,同時享受原生優先的性能和體驗。希望本文能幫助你更好地理解和使用Skip工具。
最后展望一下,通過Skip的代碼轉換能力,適配HarmonyOS Next也具備較高的可行性。