《KMM 技术在移动 App 开发中的探索与实践-袁晗光.pdf》由会员分享,可在线阅读,更多相关《KMM 技术在移动 App 开发中的探索与实践-袁晗光.pdf(36页珍藏版)》请在三个皮匠报告上搜索。
1、KMM 技术在移动 App 开发中的探索与实践分享人:袁晗光袁晗光袁晗光百度资深研发工程师百度资深研发工程师从事软件开发 16 余年,2013 年入职百度,目前负责百度 App 和其他多个创新类 App 功能迭代与框架设计方向,长期从事研发质量、效率间的平衡探索。编码成本高,需降本增效。相同的业务逻辑双端需用不同的语言各自实现一遍。双端业务逻辑的实现完全一致还很难做到。同样的业务逻辑仅靠口头上或文档上方案对齐导致最终编码实现上有着不小的差别。双端体验上有着不小的差别,数据上产生的不一致性导致不便解释其合理性(交互体验、收益回顾)。双端业务逻辑实现方式不一致导致修改点不能完全对齐、同步。后期升级
2、维护、测试成本都较高。UI 代码没有很好的和业务逻辑代码解耦合,造成业务逻辑代码复用困难,不方便做单元测试,组件间的循环依赖增多。业务逻辑变更需要拉上双端的研发都对齐一遍,然后各自编码实现一遍。同样的业务逻辑双端都需要测试验证一遍。问题背景问题背景1.概述概述I.KMM 基本原理介绍II.同类跨平台框架对比 Flutter&React Native2.KMM KMM 开发环境介绍开发环境介绍I.环境配置&项目结构II.特定于平台的实现方式 expect/actual III.如何引用已有组件 API IV.如何在已有的工程中集成 KMM AAR&Framework 3.重点技术重点技术I.多线
3、程 模型、状态共享、可变性、Atomic 类II.基础库建设:网络、数据库 4.实践落地实践落地I.落地情况介绍 目录目录01Kotlin Multiplatform Mobile,其中 iOS 端基于 Kotlin Native(简称 KN)技术,Android 端基于 Kotlin JVM,利用 KMM 技术可以使用 Kotlin 语言技术栈在 iOS 和 Android应用程序之间共享通用代码,只在必要时编写特定于平台的代码,用来构建统一的业务逻辑代码。1.1 KMM 1.1 KMM 基本原理基本原理 简介简介 1.21.2 同类跨平台框架对比同类跨平台框架对比 KMMKMM的优势的优势
4、 无需内置多套引擎(runtime),包体积增量更少。对于 Android 开发者无需多学习一套编程语言和编程思想,门槛更低。基于双端标准组件输出,审核被拒风险较小(iOS)。更强的互操作性,支持与本地编程语言的双向互操作,可以直接使用现有库,避免了众多基础组件的重复建设。FlutterReact Native022.12.1 环境配置环境配置 安装工具:Android Studio(建议官方最新版)Kotlin 插件 Kotlin Multiplatform Mobile 插件 Xcode(12.5 版本及以上)JDK(8 及以上)2.1.22.1.2 创建跨平台创建跨平台AppApp步骤:
5、在Android Studio中,选择文件|新建|新建项目。在项目模板列表中选择Kotlin Multiplatform App/Library。为应用程序指定名称。保留应用程序和共享文件夹的默认名称,并在 iOS 框架分发选项列表中选择常规框架。2.1.22.1.2 项目结构项目结构三个部分组成:共享模块-包含Android和iOS应用程序的核心应用程序逻辑:类、函数等。构建到 AAR&Framework 中,使用Gradle作为构建系统。(commonMain、androidMain、iosMain)iOS应用程序中的Xcode项目 Android应用程序中的Kotlin模块.2.2 特定
6、于平台的API 和实现 expect fun getDeviceModel():Stringactual fun getDeviceModel()=android.os.Build.MODEL/Xiaomi 10 actual fun getDeviceModel()=UIDevice.current.model/iPhone 11,6 expect/actual 机制:/获取手机型号字符串的接口基本流程:在 commonMain 目录下建立一个 expect 类或 Top-Level 方法,类似创建一个协议声明。分别在 androidMain 和 iosMain 目录中,创建与 expect
7、 声明(类名、方法名、参数类型及名称)完全一致的实现,否则编译器会报错。2.3.12.3.1 如何引用已有组件如何引用已有组件 -Android 直接在 androidMain 后的闭包中,按照 Gradle 规范添加依赖即可在 androidMain 目录下的 Kotlin 代码中调用依赖库中的类/方法。2.3.22.3.2 如何引用已有组件如何引用已有组件 -iOSiOS 使用 cinterop 扫描 Apple Framework,根据.h 文件获取可以调用的类、方法、变量、常量及他们对应的类型,最终生成 klib 文件,如需使用 Swift 的方法,需要添加 objc 前缀。官方提供的
8、Framework 引用配置比较复杂,目前已自研 EasyBox KMM 插件,可一键引用 EasyBox 组件。cinterop.h.knm2.3.32.3.3 如何引用已有组件如何引用已有组件 -EasyBox KMM Gradle EasyBox KMM Gradle 插件插件cinterop 配置复杂根据官方文档的说明,build.gradle.kts 文件内需要编写大量的 cinterop 配置iOS 依赖不便于管理直接引入二进制(Framework),无法对其进行版本控制,也可能导致冗余文件产生产物发布复用不便原生 KMM 工程默认不具备发布能力,不便于集成在目标宿主中,以及组件化
9、建设 2.3.4 2.3.4 如何引用各自已有组件如何引用各自已有组件 -EasyBox KMM Gradle EasyBox KMM Gradle 插件插件的使用的使用2.42.4 在已有的工程中集成在已有的工程中集成 KMMKMM KMM 的最终产物是 AAR(Android)和 Framework(iOS)033.13.1 多线程并发多线程并发 协程(kotlinx-coroutines)优势:语法比较简洁,可直接使用不足:对 Kotlin Native 支持不够完善(尤其是多线程的实现)第三方库(KMM 官方推荐 CoroutineWorker 和 Reaktive)Coroutine
10、Worker 是对 Kotlin 协程的封装,迭代比较少,不算比较稳定的方案Reaktive 采用 RxJava 的实现思想,Native(iOS macOS)底层采用 Kotlin Native 实现,功能较多,但框架相对较重 利用 expect/actual 方法+Block,Android 端可以利用线程池,iOS 端可以使用 GCD 自行实现优势:使用了各平台已有比较成熟的多线程方案,更稳定,运行效率更高不足:需要一定的基础能力建设,需要编写一些平台差异化代码3.1.13.1.1 模型模型3.1.23.1.2 多线程间共享状态的规则多线程间共享状态的规则规则1:可变状态仅单个线程可见。
11、规则2:不可变状态多线程可见。不变和冻结状态:被 freeze 的实体,其所有属性都不可被修改。被constconst修饰的常量。在一个对象上执行 freeze()会变成 frozen 状态,该对象所引用的其他对象都会变成 frozen 状态(含容器)。freeze()操作是不可逆的,如果需要获得非 frozen 的对象,只能将原先被 froze 的对象进行深拷贝,成为一个新的对象。名词解释:状态、可见3.1.33.1.3 多线程间共享状态的规则多线程间共享状态的规则data class MoreData(val strData:String,var width:Float)data clas
12、s SomeData(val moreData:MoreData,var count:Int)/.val sd=SomeData(MoreData(abc,10.0),0)sd.freeze()3.1.43.1.4 多线程间共享状态多线程间共享状态 -Top-levelTop-level 对象对象ObjectObject单例:单例:默认在创建后就会被 freeze,对所有线程可见,但如果修改其内部引用,将会导致报错。/Kotlin object ObjectState var num=0 fun add()num+/此行代码执行时,将抛出异常 /Swiftlet obj=ObjectState
13、.init()DispatchQueue.global().async obj.add()如果需要在不同的线程中对其进行修改,用 ThreadLocal 对其进行注解,这将允许它是可变的,并为每个线程提供其状态的副本。ThreadLocal object ObjectState var count=0 fun add()count+/OK3.1.53.1.5 多线程间共享状态多线程间共享状态 Top-levelTop-level 属性属性TopTop levellevel 属性:属性:默认仅对主线程可见,且是可变的,从其他线程访问将引发异常。/TopLevel.ktvar topLevel=“
14、Test top level properties without annotate./TestDispatchQueue.global().async print(TopLevelKt.topLeve)/kotlin.native.IncorrectDereferenceException:Trying to access top level value not marked as ThreadLocal or SharedImmutable from non-main thread注:注:Top Level 的 val 变量被认为是 const,默认也是 frozen 状态。注意:Top-
15、level 属性必须在主线程初始化!如何让其他线程可见?3.1.63.1.6 多线程间共享状态多线程间共享状态 -Top-Top levellevel 属性属性两种注解来标注 Top level 属性 SharedImmutable:全局范围可见,但是被 freeze 无法修改。(此注解只能用于 val 属性)ThreadLocal:给每个线程提供独立的可变副本。(此注解可用于 val、var 属性)/TopLevel.ktSharedImmutableval topLevel=“Test top level properties with SharedImmutable annotate.如
16、何让其他线程可修改?3.1.73.1.7 如何实现可变性?如何实现可变性?重点回顾重点回顾 可变的非冻结状态只有一个线程可见。不可变的冻结状态可以被多线程同时访问。如何绕过这些限制?如何绕过这些限制?Atomics(原子类)Thread-isolated states(线程隔离状态)Low-level capabilities(略)3.1.83.1.8 如何实现可变性?如何实现可变性?-Atomics (StatelyStately库)库)1、AtomicInt/AtomicLong:持有共享的 Int/Long允许多个线程读写。object SomeState var count=0 fun
17、 add()count+/This will throw an exception object AtomicDataCounter val count=AtomicInt(0)fun addOne()count.increment()/its ok 示例:2、AtomicReference:持有一个对象实例允许多个线程读写。示例:object GlobalData var sd=SomeData(0)fun storeNewValue(i:Int)sd=SomeData(i)/触发 Crash 异常 data class SomeData(val i:Int)object GlobalDat
18、a val sd=AtomicReference(SomeData(0).freeze()fun storeNewValue(i:Int)sd.value=SomeData(i).freeze()3.1.93.1.9 如何实现可变性?如何实现可变性?-实际应用 /对外暴露一个普通变量,但实际的 get/set 方法转接到 AtomicReferenceclass ThreadSafeData var userName:String?get()return _userName.value set(value)_userName.value=value private val _userName=
19、AtomicReference(null)循环强引用&内存泄露 性能问题,AtomicReference 在频繁对引用的对象进行修改操作的情况下会对性能造成较大的影响,不建议使用该类实现,但如果需要实现 MutableList 或HashMap 时,该如何操作?3.1.103.1.10 如何实现可变性?如何实现可变性?-AtomicReference 的内存泄露内存泄露 3.1.113.1.11 如何实现可变性?如何实现可变性?-线程隔离状态线程隔离状态隔离状态隔离状态:将可变状态隔离到单个线程,并允许其他线程与该状态通信。IsoArrayDeque 的用法:创建一个对线程具有独占访问权限的工
20、作队列,并创建一个仅存在于该线程中的可变状态。其他线程通过调度工作队列上的工作与可变线程进行通信。AtomicReferenceList 和 IsoArrayDeque 该用谁?对于单个的值,AtomicReference 是更好的选择。对于值的集合,使用线程隔离状态是更好的选择,主要的性能损失实际上是跨线程。3.23.2 基础库建设基础库建设 ,网络、数据库网络、数据库 在实际更复杂的业务场景中,需要一套丰富、稳定、可靠的跨端基础能力才能发挥出 KMM 的核心优势。充分利用百度 APP沉淀的各类组件和能力,避免了重复建设。双端接口差异化抹平,使用起来更方便,更有利于以保证双端逻辑代码编写的一致性。04落地情况介绍落地情况介绍 多个产品线:百度App及其矩阵产品。KMM:31%OC/Swift:69%