多平台移动开发新方案KMM
背景
在过去的几年里,跨平台开发技术层出不穷,例如React Native、Flutter和Xamarin等。这些技术都试图解决在多个平台上共享代码的问题,以提高开发效率和降低维护成本。 然而,它们各自都有一定的优缺点:
React Native:基于JavaScript与原生组件映射调用,拥有庞大的社区支持,可实现较高的代码复用率。但JavaScript性能相较于原生开发略低(新架构仍未正式发布),且受原生组件平台特性和API的支持方面局限。
Flutter:使用Dart语言,拥有独特的UI框架,可以快速构建美观的界面。但是,它的生态系统相对较小,一些特定平台的功能可能需要编写原生代码。
Xamarin:基于C#,可以实现较高的代码复用率。然而,它的开发环境和工具相对较重,可能导致开发和调试过程中的困扰。
KMM简介
KMM的诞生离不开它的“母语”——Kotlin。Kotlin是一种基于JVM(Java虚拟机)的现代编程语言,旨在提供更简洁、安全和高效的代码编写体验。自2017年Google将Kotlin宣布为Android官方开发语言以来,Kotlin已在全球范围内迅速获得了广泛的认可和应用。
Kotlin团队也在积极推动多平台化, 2017年11月发布的Kotlin 1.2中,引入了实验性Kotlin Multiplatform Project(KMP),同时支持server(Java),web(JS),desktop,苹果系列(macOS,tvOS,watchOS,iOS)及android.
KMM(Kotlin Multiplatform Mobile)则相当于是KMP的在移动端(iOS及android)上的支持.
原理
KMM通过Kotlin/Native编译器生成平台特定的二进制文件。对于iOS平台,Kotlin/Native将Kotlin代码转换为LLVM中间表示(Intermediate Representation,简称IR),然后编译为与Objective-C和Swift兼容的二进制文件(.o->.framework)。
对于Android来说就是.class字节码文件(jar->dex)。通过这种方式,KMM实现了在保留原生UI和性能优势的同时,在不同平台上共享业务逻辑代码。开发者可以在共享代码和平台特定代码之间灵活切换,根据项目需求进行调整,充分利用KMM的优势。
简单来说,就是编译器魔法,然后走各自平台的打包流程,与原生无异。所以KMM与原生有一摸一样的优缺点,但是共用了逻辑层.
项目创建及结构
1.我们可以通过在AndroidStudio选择安装官方提供的Kotlin Multiplatform Mobile
插件即可方便地创建KMM工程。
2.安装完插件后,打开Android Studio,选择Create New Project –> 在项目向导中选择KMM Application,点击Next –> 输入项目名称(如KMMdemo)和包名,然后点击Finish,即完成项目创建。
工程分层及项目结构如下:
从项目结构及目录上可以看出KMM与其它跨平台技术不一样,它更侧重同一的逻辑层,同时在逻辑层上提供对应平台目录,提供用户需要的平台特性自定义拓展。UI层开发者使用各自平台的原生UI体系。
由于Kotlin已经是Android官方推荐语言,所以KMM在Android端支持就不再赘述.
拓展及支持
- 官方已将iOS SDK库默认导入,所以如需要可以在shared层中的iOSMain使用使用如UIKit,Foundation,等API.KMM项目中UI层Swift代码/Swift UI可以无缝调用share中 kotlin代码,而share层中可以无缝调用oc代码,且均提供IDE插件支持,也就是所可以直接在Kotlin中直接调用iOS SDK的相关接口.
- 对于SDK以外的常用第三方库,如
AFNetworking
,则可以在shared下的build.gradle文件下通过cocoapods
节点引入,代码如下1
2
3
4
5cocoapods {
...
pod("AFNetworking", "4.0.1")
...
} - except/actual机制,提供平台自定义拓展。通过以上介绍后,我们可以发现基本上原有的原生开发的所有库,我们都可以直接使用而不用重复造轮子。
但还有一个问题就是,如果直接将原生API提供给UI层使用时,由于API不一致,UI层还需要判断是android平台还是iOS平台非常繁琐。于是有了强大的except/actual拓展机制,以下以获取系统版本及系统名称为例
- shared/commonMain中定义expect2.shared/androidMain中定义Android端actual实现
1
2
3
4
5
6
7package com.wonder.kmmdemo
interface Platform {
val name: String
}
expect fun getPlatform(): Platform3.shared/androidMain中定义iOS端actual实现1
2
3
4
5
6
7package com.wonder.kmmdemo
class AndroidPlatform : Platform {
override val name: String = "Android ${android.os.Build.VERSION.SDK_INT}"
}
actual fun getPlatform(): Platform = AndroidPlatform()我们可以从以上代码看出定义了统一的except定义,具体实际实现分别实现于各自平台,从而屏蔽API不一致引发的代码问题。UI上层调用时只需使用1
2
3
4
5
6
7
8
9
10package com.wonder.kmmdemo
import platform.UIKit.UIDevice //直接无缝调用iOS相关API
class IOSPlatform: Platform {
override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}
actual fun getPlatform(): Platform = IOSPlatform()com.wonder.kmmdemo.getPlatform().name
即可实现统一跨平台调用.
现状
Kotlin Multiplatform is in Beta. It is almost stable, but migration steps may be required in the future. We'll do our best to minimize any changes you have to make.
虽然KMM现在仍为Beta阶段,但Kotlin团队宣布会于2023年发布稳定版.- 在过去的几年里,KMM不仅优化了编译器和工具链,还引入了许多新的库和框架,如Ktor、SQLDelight和Koin等,以满足开发者们日益丰富的需求。同时KMM的社区也在不断壮大。随着Kotlin语言本身的普及,越来越多的开发者开始关注并参与到KMM项目中,社区涌现了一大批优秀的三方库。
- KMM在全球范围内的普及程度逐渐上升。越来越多的企业和开发者选择KMM作为他们的跨平台开发解决方案。国内外众多企业已开始使用KMM技术 除此之外,国内携程及美团等团队也均有在业务中进行尝试,具体情况可移步其对应技术博客.
缺陷及展望
通过以上分析我们可以发现,其实KMM目前相当于只实现了半跨平台(即仅在UI层实行统一共用).但通过特性及拓展支持,我们可以预见它完全有能力实现全跨平台支持(即剩余的UI层支持),个人觉得至少有2种方式,可以采用像RN一样的原生平台映射方式(但会受各自平台特性,UI风格,设备版本差异等影响),也可以采用像Flutter一样,重新定义一套UI架构,提供统一的新的API。
Kotlin团队明显选择了后者,他直接使用了Google jetpack compose作为对上层提供的新UI层API,然后添加iOS平台实现,从而达到跨平台UI效果,于是Compose Multiplatform横空出世,补齐了KMM跨平台最后一块版图。
于是项目结构就可以变为
目前为止Compose Multiplatform于今年4月14日发布alpha版(主要是iOS平台,android及desktop已经为稳定版)
并计划于明年发布稳定版,个人觉得beta版,应该今年内可完成
由于篇幅所限,届时如有机会将另起一篇为小伙伴们介绍Compose Multiplatform.