大家好我是黄林晴,也是图书《Android Jetpack开发:原理解析与应用实战》的作者。上一次在社区分享还是在三年前的Android 11见面会上,本次为大家分享的主题是Compose Multiplatform和KMM。这里是本次分享的文字版。
上个月JetBrains 发布了 Compose Multiplatform for iOS Alpha 版本,这让许多热爱跨平台的开发者喜出望外。但是也有许多开发者可能还没有了解过Compose Multiplatform和KMM,那么本次分享将通过以下几点来介绍Compose Multiplatform 与KMM,让我们一起体验Kotlin跨平台的魅力。
这里需要先说明的是,本次分享我们只会从使用的角度去分享,作为一次跨平台技术的普及,不会涉及跨平台的底层原理,比如为什么可以跨平台这些深奥的道理。为什么不讲这些呢?原因很复杂,简单的说就是我不会。
要Compose Multiplatform 与 KMM的关系,我们只要来分别了解Compose Multiplatform 与 KMM分别是什么就行了。
KMM的全称是Kotlin Multiplatform Mobile,与之对应的是KMP—Kotlin Multiplatform Project,其实就是一个是Kotlin移动端跨平台,一个是Kotlin跨平台项目的集合。KMM更像是营销术语,我们不用纠结Mobile这个词,你要知道的是,下文我们所说的KMM就是指的Kotlin跨平台不仅限于移动端就行了。
KMM可以简化多平台应用程序的开发。通过KMM,开发者可以在 iOS 、 Android、Desktop与Web 应用程序之间共享业务逻辑的通用代码,在必要时也可以编写特定于平台的代码。所以,KMM只负责跨平台下的业务逻辑部分。
比如这张图中的数据层、网域层等都可以使用KMM来完成公共的业务逻辑。
在KMM早期推出来的时候,那个时候Compose Multiplatform还没有发布,所以大家都觉KMM很鸡肋,因为90%的开发者认为移动端的主要工作都在编写UI上,跨平台不能跨UI叫做哪门子的跨平台?
但是,其实这种观点是不正确的,很多业务逻辑比如日志系统、埋点等业务使用KMM还是非常有利的。
后来Compose Multiplatform的出现弥足了KMM的短板。那么Compose Multiplatform又是什么呢?
Jetpack Compose是Android官方推出的声明式UI框架,Compose Multiplatform是由JetBrains维护的Compose跨平台框架,专注于UI跨平台,同样支持iOS、Android、Web、Desktop等。
有了Compose跨平台,便弥补了KMM的缺陷。
我始终觉得有一个尴尬的问题就是,要说Compose Multiplatform与KMM不是一个东西吧,他们确实不是一个东西,毕竟版本的更新、维护者都不同。但是毕竟Kotlin底层对Native、JS的支持都是Compose Multiplatform的基础。所以我更希望有一天他们可以合并,不管是版本的更新还是插件的支持都可以统一。所以我更喜欢直接称他们为Kotlin全平台。
那么其实,你现在也已经知道了KMM与Compose Multiplatform的关系。接下来我们来看Compose Multiplatform 与 KMM是如何实践的。
由于Compose Multiplatform 与 KMM一个复用UI一个复用业务逻辑,所以我们分别来看是如何实践的。
KMM用于实现业务逻辑部分,这里我们只以Android和iOS两端为例。这里还是要再次重申一下,Compose Multiplatform 与 KMM是可以独立使用的,也就是说在使用KMM的时候可以分别写Android、iOS的原生UI,在使用 Compose Multiplatform跨平台的时候也可以分别实现其他端的业务逻辑。
在Android Studio中我们可以借助Kotlin Multiplatform Mobile plugin插件来快速的创建支持KMM的项目。
安装好插件后,打开Android Studio我们可以直接创建支持KMM的项目。
创建的时候会让我们填写模块的信息
创建好项目后,生成的项目目录结构是这个样子的。
androidApp、iOSApp就是对应的Android、iOS各自的代码库,shared模块,即存放Android、iOS公共业务逻辑的部分。
KMM插件只为我们创建了Android和iOS的源集,如果想创建其他平台的可以自己创建文件夹然后指定目标平台。
创建好项目之后我们来看如何处理公共的业务逻辑。
双端完全可以共用的逻辑我们直接放在commonMain文件夹下即可。开源库的依赖我们写在commonMain目录下。
这里添加网络请求库Ktor和序列化的依赖,因为是Kotlin跨平台嘛,Ktor是Kotlin推出的网络请求库,所以肯定使用Ktor是最佳选择。
这段代码呢,就是Ktor这个网络请求框架的基本用法,我们不做过多解释,在这里我们定义了一个getData方法,用于获取「鸿洋」大佬「wandroid」中的「每日一问数据」。
然后我们各自在编写Android或者iOS的UI代码接收数据即可。我们这里直接将返回展示展示在文本中,最终实现的程序是这个样子的。
这个UI我们将在后面的Compose Multiplatform 中实现。这样我们就实现了双平台一个简单的数据请求的例子。
目前官方许多库都已经支持了跨平台,比如我们刚刚使用的网络请求框架Ktor、依赖注入Koin还有序列化组件等。
除此之外,对Android开发开发来说,最友好的消息是从去年10月份开始Jetpack也开始支持跨平台了,不过当前Jetpack支持的跨平台组件只有三个:Annotations、Collections、DataStore。
当然还有一些比较有名的App也开源了一些组件比如Cash App开源了Paging分页库。
与AndroidX下的Paging设计一样,paging-common模块提供存储层、视图模型层;paging-runtim模块提供UI层。
最主要的是,paging-common中的API与AndroidX 下的API完全相同,仅仅是将包从androidx.paging迁移到了app.cash.paging中,所以这部分的使用我们直接按照AndroidX中的Paging使用即可。
但是在实际项目中,仅依靠社区的支持可能没办法满足所有业务。当然也有一些开源贡献者开源了一些组件,但是为了确保稳定性,我们一般需要自己去单独实现各自的业务逻辑,那么我们如何确保使用同一套API呢?
我们要依赖Kotlin中的expect与actual关键字。expect是我们期望实现的方式,actual是实现方式,有点类似接口与实现类。比如我们现在要实现业务逻辑:打开系统蓝牙。
首先我们要在commonMain中使用expect定义这个接口
然后我们在shares模块下的androidMain、iOSMain目录下各自实现打开蓝牙的方法。
这样我们就确保多平台下使用同一API来调用,调用方不需要关注具体的实现。那么到这里呢,KMM我们就了解的差不多了,从上面的了解可以看出 其实KMM当前是可以使用在实际项目中的,不过我们可以再等等,Kotlin的RoadMap中说今年会发布正式版本,我们可以一起期待一下。接下来我们再来一起实践Compose Multiplatform。
Compose Multiplatform 专注于UI复用,我们前面提到过,有个尴尬的问题就是KMM与Compose Multiplatform 的版本和插件是不统一的。我们可以借助KMM插件在Android Studio中快速的创建KMM项目,但是当前如果我们想快速创建Compose Multiplatform 项目只能借助新版的IDEA。
下载最新版本的IDEA,创建Compose Multiplatform项目,但是更尴尬的是,由于当前iOS还在alpha阶段,所以IDEA并不能创建iOS平台的项目。
所以我们我们现在如果想使用Kotlin全平台有两种方式:
这里我基于刚刚创建的KMM项目,在KMM的基础上添加Compose Multiplatform的配置。
项目配置好之后,我们接着刚刚查询每日一问的功能来实现,当然在配置的时候肯定踩了很多坑,这些我都记录在我的博客中了。
iOSApp.swift中的代码是这个样子的。
Main_iosKt.MainViewController是通过新建在shared模块iOSMain目录下的main.ios.kt文件获取的,代码如下所示:
所以,我们可以在shared模块中的commain目录下编写解析网络数据并现实的Compose方法,然后在Application下调用就行了。
这个代码大家肯定都能看懂,和Jetpack Compose是完全一致的。通过Message方法将数据展示出来,这里只将作者与标题内容显示出来,代码如下所示:
然后这样我们就可以运行Android和iOS程序了,这里要注意的是借助KMM插件我们可以直接运行iOS程序,但是有个前提就是仍然要安装Xcode,Android和iOS效果如下图所示。
我们可以看到页面效果是完全一致的,并且就页面中这个功能来说,达到了业务逻辑和UI的百分之百复用。怎么样是不是泰酷辣!
我们上面都是以Android和iOS为例,其实Desktop与Web端也是一样的,相对比来说我觉得Desktop已经比较成熟了,UI也是可以完全复用Jetpack Compose。我又分别实现了Desktop与Web在此示例上的效果。
这里对Web要多说一点,在早期的时候Compose for Web是使用Compose HTML来实现的,Compose HTML 是一个面向 Kotlin/JS 的库,它提供了用 HTML 和 CSS 创建 web 用户界面的 Composable 组件,所以割裂问题非常严重,不能说不能与Jetpack Compose复用,只能说和他毫无关系。
比如我们实现图中的数据显示Compose HTML写法是这样的,当时看到这个是比较崩溃的。好在Kotlin在1.8.20版本中推出了Kotlin/Wasm,最新的Compose for Web 是基于Kotlin/Wasm的,当前处于试验阶段。页面可以完全与Jetpack Compose复用,所以大家注意一下Compose HTML的写法就不要再学习了。
官方给出了一些Compose Multiplatform的模版,也有Kotlin/Wasm的模板,但是唯独没有Compose Wasm for Web的模板,所以,我自己在github上开源了一个模板,感兴趣的大家可以去使用。
和刚刚提到的组件问题一样,随着Compose Multiplatform技术的成熟,早晚官方会推出一个新的插件来同时支持KMM和Compose Multiplatform。
在使用Jetpack Compose开发Android的时候,有些场景下我们可能需要让Jetpack Compose与XML 嵌套使用,那么在跨平台中肯定也会存在这种场景,在iOS中可以通过使用 UIKitView,在共享用户界面中嵌入复杂的特定于平台的小部件,如地图、 Web 视图、媒体播放器和照相机等。
总之,这些肯定都不会是阻碍Compose Multiplatform跨平台发展的因素。
当前与Compose跨平台竞争的主要主力应该是Flutter,很多人总喜欢将他们进行比较,现在比较肯定是Compose Multiplatform肯定不如Flutter的,但这样比较也有点欺负Compose Multiplatform。毕竟Compose Multiplatform还很年轻。
当然这是一个非常开放的话题,我只表明个人观点。Flutter永远都会存在语言壁垒的问题,但是KMM和Compose Multiplatform对Android开发者来说几乎是赠送的。当然了现在存在两拨人可能阻碍他们使用KMM和Compose Multiplatform。
对于没有使用过Jetpack Compose的这部分人来说,其实我是可以完全理解的,一些组件的支持,比如地图、WebView等可能还需要一定的时间,毕竟现在使用XML是完全没有问题的。还有一部分人呢是没有过Kotlin。
排除从事Farmework或偏低层的这部分人,其实我是完全没有办法理解甚至是有些无语的。很多人告诉我的理由都是Java也能用啊、老板不让用啊、公司项目陈旧啊,其实这些放到现在都是借口了。
已经在使用Kotlin的,我建议可以学习下Jetpack Compose,一来这是一个趋势,二来它会扩展你的跨平台技能。如果你想在未来几年内仍然从事Android开发,我觉得是没有理由拒绝的。
最后,不破不立,这句话与大家共勉。
好了。本次的分享到这里就结束了,大家可以关注我的公众号《Android 技术圈》,我将持续分享跨平台等Android领域的技术~,期待我们下次再见!