干货 | 携程机票 App KMM 跨端生产实践

作者简介

 

禹昂,携程移动端资深工程师,Kotlin 中文社区核心成员,图书《Kotlin 编程实践》译者。

Derek,携程资深研发经理,专注于移动端开发,热衷于各种跨端技术的研究和实践。

一. 背景与选型

移动端跨平台技术自移动开发诞生以来一直是个热门话题,一是持续关注研发效率,降本提效;二是一套代码多端运行可以提升多端业务逻辑的一致性;三是跨端技术方案通常意味着更佳的高效运维和缺陷修复。

跨平台开发框架经过多年的发展,目前被行业采用率最广的应属 Facebook 的 React Native,而当前最被大家寄与厚望的则是 Google 的 Flutter。这两者虽然在设计及原理上区别很大,但设计思想上都是采用非原生开发语言在 Android 与 iOS 系统框架之上搭建的“阁楼”上运行,每个采用这些框架的 App 在打包时需要集成语言的 Runtime、框架的底层组件等许多重量级的包与库。并且 JavaScript 或 Dart 与原生开发语言(Java/Kotlin、Objective-C/Swift)之间的交互需要通过“桥接通讯”实现,导致每当需要系统框架层面的改动支持时,必须双方模块架构上共同协调处理。

作为移动端开发人员,我们希望找到一种性能与原生代码相媲美、与原生代码互操作能力强、开发思想与原生开发接近的跨平台开发框架。

JetBrains 提出了不同于 RN 与 Flutter 的跨端解决方案,即使用不同的编译器编译同一份代码生成各端的不同产物来达到跨平台的目的,这就是 Kotlin Multiplatform。Kotlin 依据其运行的平台不同拥有不同的名字,例如编译为 class 字节码运行于 JVM 及 Android 平台的称为 Kotlin/JVM,编译为原生二进制码无虚拟机环境直接运行于操作系统上的则称为 Kotlin/Native,此外还有 Kotlin/JS 等等(关于 Kotlin Multiplatform 的官方介绍请详见参考链接 1)。

Kotlin 在不同平台均可与该平台的原生开发语言直接相互调用,在 Android 平台 Kotlin 是官方支持的一等开发语言,与 Java 的互操作自不用说。而在 Kotlin/Native 中 Kotlin 也可以像与 Java 互操作般在 iOS 平台直接与 C 以及 Objective-C 代码互操作(函数、类、接口互相可见、基本类型与集合类型等可互相映射)。不过其他语言如 Swift 与 Kotlin/Native 的互操作能力较为受限,官方正逐步改进。

Kotlin 在移动端的跨平台框架子集叫做 Kotlin Multiplatform Mobile,简称为 KMM。KMM 的架构设计理念如下图所示:

e67af8880d3be9cd300ed334ac3a63d7.png

开发人员编写的代码主要分为三个 source set(源集),其中与平台直接交互的代码位于以平台命名的 source set 中,例如在 Android source set 中的 Kotlin 代码可以调用 JDK、Android SDK、以及其他 Android/Java 开源库,而在 iOS source set 中的 Kotlin 代码则可以直接调用 iOS 平台支持的 Posix C API、Foundation、以及其他 C/Objective-C 开源库。两端通用代码则位于 Common source set。

整个工程的构建由 Gradle 驱动,在编译打包时,通过将 Common 与 Android 两个 source set 的 Kotlin 代码合并编译打包为 Android 平台产物(aar 文件)。而将 Common 与 iOS 两个 source set 的 Kotlin 代码合并编译打包为 iOS 平台的产物(framework 文件)。

与 RN 及 Flutter 等跨平台框架相比,KMM 的主要优势有:

1)移动端原生技术栈开发人员上手更快。

2)无额外的运行时环境,性能与原生代码基本持平。

3)可无缝对接现有原生基础库,基础架构改造成本较小。

4)可沿用现有的原生插件化、内存监控、崩溃/卡顿监控等基础技术,无需额外开发支持。

不过 KMM 是语言层面跨平台的技术与框架,且当前处于 alpha 阶段,所以仍有一些缺点,包括:

1)Kotlin/JVM 与 Kotlin/Native 的异步并发模型不同。

2)KMM 社区生态环境仍在建设中,没有成熟的 UI 框架,因此无法用于编写 UI。Kotlin 编译器仍然处于快速迭代升级阶段,因此元编程相关的 API 不稳定。

2020 年携程机票 Android 团队将核心业务的历史 Java 代码迁移至 Kotlin + Coroutines + Jetpack AAC 技术栈获得了不错的成效,详见《携程机票 Android Jetpack 与 Kotlin Coroutines 实践》。Kotlin、Coroutines、MVVM 等新型架构模式在 Android 平台经受住了千万量级访问量的生产考验,因此我们决定于 2021 年初开始尝试 KMM,将 Kotlin 的应用范围逐步扩大至 iOS 平台。


二. 总体设计与集成

由于 KMM 尚处于 alpha 阶段,初期主要定位是——实现业务逻辑代码的跨平台共享,包括:数据模型、网络请求、本地数据存储、业务逻辑处理。

如果要从零搭建一个 KMM 工程,IntelliJ IDEA 或 Android Studio 的 KMM 模版插件可以辅助创建,整体工程就是一个常规 Gradle 工程,内部包含两个 Gradle module 子模块,分别是 Android app 与 KMM module。Android app 通过工程依赖直接引用 KMM module,此外还包含一个 iOS Xcode 工程。

但我们的场景是在现有且彼此独立的携程 Android 与 iOS App工程中引入 KMM,所以我们需要将 KMM 作为一个独立子工程模块进行集成。携程的 Android 与 iOS App 工程结构大体相似,底层是公共基础团队负责的公共库及框架,上层是依赖公共框架层的各个业务团队的 bundle。KMM 作为一个独立的工程需要依赖基础库,且机票业务 bundle 依赖 KMM 跨端共享业务逻辑工程。

机票业务工程集合的 KMM、Android、iOS 三个子工程的简化版依赖关系如下图:

a15f6f84db6540554edb9ae268ac8880.png

Android 工程依赖机票 KMM 工程,通过 Gradle 构建并发布至公司内部 Maven 源的 aar 文件;iOS 工程通过本地集成 KMM 构建生成的 framework(目前正在调研迁移至 CocoaPods 集成方案)。

我们希望复用并扩展之前 Android Jetpack AAC 的优化升级成果,因此业务代码架构继续使用 MVVM 模式,整体分为三部分:View、ViewModel、Model。KMM 目前尚缺成熟可靠的 UI 框架,UI 层暂且保留原生开发方式,由平台各自实现,Model 层与 ViewModel 层由 KMM 工程承载。

2.1 Android 集成

KMM Android 端集成非常简单,与普通的 Android 第三方库集成无异。使用 IntelliJ IDEA 或 Android Studio 的 KMM 插件创建的 KMM 工程默认生成 Android source set,Gradle Build Task 执行生成 AAR 文件。当然,如果想创建一个泛 JVM 平台共享库(不涉及调用任何 Android SDK 和第三方库 API),我们可以把 Android source set 修改为 JVM source set,Gradle Build Task 就会生成 JAR 文件。

无论是新建独立 KMM App工程,还是基于现有 App工程集成 KMM 模块,KMM 子工程模块生成的 AAR 或 JAR 文件产物,均可发布上传至指定的 Maven 源仓库,进行集中依赖管理。调用方通过 Gradle/Maven 的 api 或 implementation等语句添加依赖。这对于 Java/Kotlin 开发人员非常友好,没有增加额外学习认知成本。当然对于小型个人项目,也可以使用简单的 Local Module Project 本地直接依赖方式。

KMM Module 工程集成与常规 Android Libraray Module 工程集成一脉相承,整理实践过程中遇到的若干常见问题:

1)在设置 KMM 工程的 target Java 版本时,尽量与需要集成的主工程保持一致,否则 KMM 的 target Java 版本如果过高可能会导致主工程构建失败。

2)主工程在集成 KMM 工程之后,注意设置混淆策略,否则运行时容易触发 NoClassDefFoundError 异常。

3)在使用新版 Gradle 构建时注意正确设置 duplicates strategy,否则主工程可能会集成失败。

2.2 iOS 集成

iOS 集成相比 Android 稍显复杂。iOS 开发者需要首先学习 Gradle 配置以及 Intellij IDEA 或 Android Studio IDE的基础知识。

iOS 集成的两点关键:

1)配置 KMM 工程依赖所需的 Objective-C 工程,使得 Kotlin 代码可以访问调用 Objective-C 代码,正确编译打包。

2)配置 KMM 工程编译打包生成的产物导入至 Xcode 工程,使得 Objective-C 代码可以访问调用 Kotlin 代码。

Kotlin Native SDK 已经预先内置了 iOS 系统所有的 API,开发人员需要手工处理的是将 Kotlin 代码与自行编写的 Objective-C 代码或其他第三方库代码进行桥接。这部分工作并不复杂,因为 KMM 的最终产物文件都是 iOS 系统常规的 .framework/.a 文件,原理遵循 iOS 平台开发常识,学习曲线对于 iOS 开发人员较为友好。

这里仅列举 iOS 集成过程中的若干场景问题:

2.2.1 cinterop

官方提供的 cinterop 工具可以将指定的 C/Objective-C 库的所有公开 API 封装转译为 Kotli

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值