【译】未来的APP:声明式UI+Kotlin跨平台(D-KMP)(中)

3,354 阅读7分钟

The future of apps:Declarative UIs with Kotlin MultiPlatform (D-KMP) — Part 2/3
作者:Sahil Sharma
译者:不想翻身的鱼

原文链接

基于声明式UI,Kotlin跨平台和MVI模式,分三篇文章来讲述新的D-KMP架构。

第一篇:D-KMP架构和声明式UI

第二篇:Kotlin跨平台和MVI模式

第三篇:D-KMP的分层和团队组织

最近更新:2021年4月8日

1.4版本Kotlin跨平台真的来了

随着2020年8月份Kotlin 1.4的发布,Kotlin跨平台已经不再是实验性阶段,已经是Alpha了。

现在,其实我们已经可以开始应用这项技术到我们的产品上了,虽然后面还会有一些改变和改进,但是目前的稳定性已经很好了。

如果目前还有什么会阻塞我们实现这个D-KMP项目的话,它们都来自UI部分(Jetpack Compose还处在密切开发中,但是随着Navigation组件的发布已经越来越好了)而不是KMP的部分。

由于Kotlin在JVM社区的成功,2017年谷歌宣布将Kotlin作为Android第一支持的语言。2019年谷歌将Kotlin指定为Android开发的首选语言(取代了Java)。

现在Kotlin已经演进为一个跨平台的语言,拥有将代码编译到3个不同的平台:

  • JVM(Android和服务端)
  • Native(iOS、macOS,Window,Linux)
  • JavaScript(Web,同时提供对React封装)

1_6SJzsygHSrdvdmShHrhi3Q.png

正是因为这个,我们现在可以用Kotlin开发共享代码直接运行到各个平台上。

目前有两个不同首字母用到跨平台上面:

  • KMM = Kotlin Multiplatform Mobile(只是Android和iOS)
  • KMP = Kotlin Multiplatform(也包括桌面端和Web)

自从Kotlin 1.4开始,已经有一个固定的KMM的门户入口,专门用来说明怎么在移动端开发开始跨平台。对于不熟悉Kotlin跨平台的人来说是非常好的材料。

JetBrains(不仅是Kotlin的作者还是Android Studio的作者)发布了一个KMM的AS插件,这个插件可以让开发者直接在AS里面运行iOS应用。这个其实也是非常有用的。

Kotlin并不是唯一实现跨平台的编程语言。同时也是一个非常有趣的编程语言,同时避免了很多的模板代码。它有很多你能想象的高级特性:协程,可计算属性,属性委托,扩展函数,高阶函数,lambda等等。

Kotlin很快的成为了一门主流的编程语言。用Kotlin写的代码肯定会持续数十年的。作为一个长期项目来用肯定是没问题的。

KMP vs Flutter vs ReactNative

当说起跨平台,2个主流的框架大家听的最多的可能是Flutter和React Native,这两个框架都可以让你进行共享代码,

同时你也可能听到大家不喜欢这些框架,因为它们限制了大家定制各个平台的原生UI。

Kotlin跨平台的到来,同时提供了这两个优点:

  • 在所有平台共享代码
  • 能自由的定制各个平台的UI

1_-pMVyErQTkoesaI97jdiLw.png

KMP里面,共享的代码是用Kotlin写的,但是最终会编译成一个原生的库:Android上是一个jar包,iOS上是一个OC的framework,web上是一个js的库。因为这样,原生的UI层在各自的平台上可以很自然和共享代码进行交互。

Flutter里面,代码是用Dart来写的,最总会编译成一个原生的库,在Android 上是通过NDK,iOS上是LLVM,Web上是JS。但是与KMP不同的是,Flutter需要开启一个自己的引擎,会对包体积的影响比较大。 Flutter不使用原生的UI,它使用的是自己的声明式UI widget是通过Skia的图像引擎一个像素一个像素画出来的。这两年有很多人在用Flutter,因为Flutter提供的声明式UI方式而不是传统的Android和iOS的View。但是现在声明式的UI已经在Android和iOS的原生端开始加速支持了,这样Flutter的主要优势已经不在了。Jetpack Compose和SwiftUI 可以让你全速的构建一个顶级的APP。

React Native,代码是用javascript写的,只能通过运行JS代码的C/C++桥跟native 层进行通信。UI的组件是对Android和iOS原生组件的封装,开发者对于UI的控制非常有限。RN整个的架构也证明性能不是特别好,甚至Facebook自己也慢慢的要放弃RN了。2018年AirBnB就宣布RN的时代结束了

语言很重要:Kotlin vs Dart vs JavaScript

另外一个KMP相对于Flutter和RN的优势就是编程语言。与Dart和JS相比,Kotlin是下一代顶级语言,它具有可靠性和简洁的特点。在Kotlin很轻松的就能写出高质量的代码,因为她具备了非常智能的特性,比如协程,可计算属性,高阶函数等等。

D-KMP架构下平台特有的代码只有15%

此时可能有人会认为“好,我知道KMP很牛。我知道声明式UI最终会来到Android和iOS平台。但是这种方式我仍然还要给各自平台画UI,这也会造成很多重复工作!”

答案是“NO!并没有很多重复的工作”

在新的原生声明式UI平台下,UI层是非常轻的。在我们目前用D-KMP架构构建的APP里,从整个代码量来看UI层大约值占了15%的代码量。并且UI是所有的平台特有的代码,其他的全是KMP的共享代码。

1_V4g54su84IaK84QdlHn7OA.png

这额外的15%的代码是完全值得的,因为它可以让我们给各个平台自由定制,而不是像Flutter和RN有很多的限制。Android和iOS是两个不同的平台有很多的不同。一个顶级的APP需要保持各个平台真实的UI/UX模式的。

从我们的经验来看,一旦我们在Android端写了Jetpack Compose的UI,就可以直接到iOS上SwiftUI实现出来。代码的结构几乎是一样的。对于一个简单的APP,要不了一天时间。

在两个框架还有一些相同的组件,虽然名字不同。例如,你想要把多个文本水平放置在一个布局内,Android端Jetpack Compose是Row,iOS端SwiftUI是HStack,文本的组件在两个框架里面都是Text,只是语法有点不一样。一旦你熟悉了这些小小的不同点,完成一端的声明式UI后,你可以快速复制到另外一个上面。

1_aBwcUfVscWvZzCaboUualw.png

所有的数据都来自于页面的状态,由KMP共享的代码提供。在各自平台的声明式UI,只需要关心view,其实是一个很轻的工作。

重要的是声明式UI不需要处理数据。啥也不用管直接展示就好。这也会大量减少平台相关的bug。

一旦共享的代码不担心bug,在各自平台上所有的事情就会推进的很顺利。这也是为什么不需要过于在意在各自平台上开发一套UI。

MVI模式:D-KMP架构的第三个核心

我们一开始就提到,MVI模式(代表Model-View-Intent)是这个结构的第三个核心部分。背后主要的概念是单项数据流,这也是它跟之前的MVC,MVP,MVVM不同的地方。MVI是响应式模式,让APP的行为更加的一致性和可预测性,你可以认为它是MVVM的演进。

1_YsJtXWJi0_xD2Nv3zZWsUQ.png

在MVI模式下,View的状态只有唯一的可信数据源。任何时候状态都是有可变的数据组成,并且只能被Model修改。所有的事情都是单向的。用户出发了一个事件/intent。Model做出相应并执行一些操作之后改变状态。然后新的状态被反映到View上面。

在我们的D-KMP架构下,我们在KMP的共享代码里面实现MVI的Model(可见下面的图)这个可以让我们在共享代码里面进行状态管理,这个非常重要!

也正是因为这样,我们平台特有的代码只是声明式UI的那一层,这一层是很轻的并且是无状态的,因为它完全把状态的管理委托给了KMP的ViewModel。

让我们看一些代码:开始我们的第三篇文章吧!