kotlin multiplatform mobile初探

kotlin multiplatform mobile programing 的探索

本文是一篇浅显的kmm 学习总结,希望对kotlin multiplatform mobile(下面简称kmm)的功能、支持平台、适用场景、使用方法、优缺点等有一个初步的认知和理解。

一、什么是kmm?

1.1 引用9月15日 kmm发布alpha版本时的一句话:

Kotlin Multiplatform Mobile (KMM) 是一个 SDK,允许您在 iOS 和 Android 应用程序中使用相同的业务逻辑代码。

两个关键字:SDK共享业务逻辑代码

1.2 详细描述

应用程序的 Android 和 iOS 版本通常有很多共同点,但也会有很大不同 – 尤其在 UI 方面。
同时,应用程序的业务逻辑,包括数据管理、分析和认证等功能,往往是相同的。
因此,标准操作是跨平台共享应用程序的某些部分,同时保持其他部分完全相互独立。
通过 KMM,您可以获得这种灵活性,并保留原生编程的优势。
将单个代码库用于 iOS 和 Android 应用的业务逻辑,只在实施原生 UI 有必要时或使用平台特定 API 时编写平台特定代码。

在官方提供的获取平台版本号、进行算数运算、网络数据I/O的案例可以看出:

(1). 向不同的平台提供一致的api接口;
(2). 共享的业务逻辑统一由kotlin编写,android直接使用,而iOS作为framework依赖为静态库后通过编写swift代码,调用暴露的API使用
(3). 各自的UI由各自实现

二、km支持的平台

2.1 支持的平台

Platformpresetcomments
kotlin/jvmjvm
kotlin/jsjsSelect the execution environment:
– browser {} for applications running in the browser.
– nodejs{} for applications running on Node.js.
Android
application/library
androidNativeArm32
androidNativeArm64
Manually apply an Android Gradle plugin
– com.android.application or
– com.android.library.
You can only create one Android target per Gradle subproject.
Android NDKandroidNativeArm32
androidNativeArm64
The 64-bit target requires a Linux or macOS host.
You can build the 32-bit target on any supported host.
iOSiosArm32 / iosArm64
iosX64
Requires a macOS host.
tvOStvOSArm64 / tvOSX64
watchOSwatchosArm32 / watchosArm64 / watchosX86
macOSmacosX64Requires a macOS host.
LinuxlinuxArm64 / linuxArm32Hfp
linuxMips32 / linuxMipse32
linuxX64
Linux MIPS targets (linuxMips32 and linuxMipsel32)
require a Linux host.
You can build other Linux targets on any supported host.
WindowsmingwX64 / mingwX86Requires a Windows host.
WebAssemblywasm32

支持的平台比较多,几乎覆盖了市场所有主流平台

2.2 工程结构

在这里插入图片描述

Project:
 |-- androidApp:   安卓应用工程文件
 |-- iosApp:        iOS应用工程文件
 |-- shared:     共享代码文件
 |-- …
   |-- commonMain:   共享逻辑
   |-- androidMain:   需要由android实现的expect
   |-- iosMain:       需要由ios实现的expect的kotlin代码
   |-- commonTest:      多平台测试
   |-- androidTest:
   |-- iosTest:
   |-- …

三、kmm的基本原理

3.1 Kotlin Native

Kotlin/Native 是一种将 Kotlin 代码编译为无需虚拟机就可运行的原生二进制文件的技术。 它是一个基于 LLVM 的 Kotlin 编译器后端以及 Kotlin 标准库的原生实现。

kotlin/Native 弥补了无需虚拟机或者运行时的自包含程序的使用场景,这是什么意思呢?因为从Android Mobile Application的角度来看问题,Android 应用时运行在JVM虚拟机上的,kotlin代码在Android平台上最终也会编译成jar的形式在JVM中执行,并且有Android Runtime的概念,而iOS是没有这些概念的,可以理解为kotlin/Native 在移动开发上是为了适应iOS编程的一种技术实现。

具体有些什么?
a. 支持那些?
在这里插入图片描述
似乎和kmm所能支持的一样,因为是由kn实现的,所以一样

b. 互操作:
kotlin/Native 可以

  • 用于多个平台的可执行文件
  • 用于 C/C++ 项目的静态库或动态库以及 C 语言头文件
  • 用于Swift 与 Objective-C 项目的 Apple Framework
// shared/build.gradle.kts
plugins {
    kotlin("multiplatform")
}

kotlin {
  android()
  ios() {
    binaries {
      framework {
        baseName = "shared"
      }
    }
  }
}

在工程中的build.gradle.kts 进行适当的配置,就可以将公共共享代码文件打包成framework供给iOS使用,并且在库(framework)中由.h 文件暴露API 方便在iOS工程中调用

此外,可以使用现有的

  • 静态或动态 C 语言库
  • C 语言、 Swift 以及 Objective-C 框架

使用cocoapods方式依赖一些现有的kotlin multiplatform library, 然后在iosMain中导入即可调用相应库中的API
支持从github、git、本地等方式pod,需要在xcode的profile中加入一些配置。

除了已经自动依赖的KN,目前可用的官方common lib:(主要是一些存储、SQL操作、协程和并发、网络、断言、数据序列化和反序列化)

plugins {
    kotlin("multiplatform")
    // id("org.jetbrains.kotlin.native.cocoapods")
    kotlin("native.cocoapods")
}
version = "x.x"
kotlin {
    ...
    ios()
    cocoapods {
        ios.deploymentTarget = "target version"
        summary = "Some description for a Kotlin/Native module"
        homepage = "Link to a Kotlin/Native module homepage"
        pod("Cocoapods dependency name", "version", podspecFile, "Kotlin/Native module name")
    }
	...
}

3.2 LLVM

一种与虚拟机无关的支持任意编程语言的动态、静态编译的项目

3.3 expect/actual

在大部分引用第三方库的场景中,Android/iOS使用的文件、方式并不相同,或者一些系统API调用上有许多差异,在遇到这种差异时,需要使用expect/actual的方式来处理:
在这里插入图片描述

举个例子:

// commonMain
expect class Platform() {
    val platform: String
}

// androidMain
actual class Platform actual constructor() {
    actual val platform: String = "Android ${android.os.Build.VERSION.SDK_INT}"
}

// iosMain
import platform.UIKit.UIDevice

actual class Platform actual constructor() {
    actual val platform: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}

其中==“platform.UIKit.UIDevice”== 是由kotlin/Native 获取到的NSObject,而NSObject是一个object-c对象,实现了kotlin使用object-c库的方法。

最终,Kotlin 代码共享代码将转换为源代码(iOS 上的本地可执行代码,使用KN/LLVM) ,在iOS工程中作为framework,在 iOS 上运行。
第三方和项目本地封装的功能API需要通过在kotlin build.gradle.kts通过cocoapods配置,并导入实现在iosMain中调用,Android则照旧。

KN中包含的一些ios内容

CoreAudio
MediaPlayer
MessageUI
ModelIO
Network
OPENGL
Photos
PhotosUI
PushKit
QuickLook
UIKit …

举个🌰 :

// Kotlin/Native Photos.kt
public open external expect fun fetchCollectionsInCollectionList(
	collectionList: platform.Photos.PHCollectionList, 
	options: platform.Photos.PHFetchOptions?
): platform.Photos.PHFetchResult { /* compiled code */ }

/*** 可以在iosMain中调用,根据指定的options(可以配置AssetSourceTypes, limit等)获取图片文件集合
 * 而androidMain仍然使用contentProvider获取
 * 上述作为两个actual fun,而在commonMain中定义一个expect fun
 */

// shared/commonMain/MediaLibrary.kt
expect class MediaLibrary {
	fun fetchPhotosCollection()
	fun updateSelectState(...)
	fun productThumbnails(...)
}

// iosMain/MediaLibrary.kt
import platform.Photos.PHCollection

actual class MediaLibrary {
	actual fun fetchPhotosCollection() {
		PHCollection.fetchCollectionsInCollectionList(collection, options)
		...
	}
	...
}

// androidMain/MediaLibrary.kt
actual class MediaLibrary {
	actual fun fetchPhotosCollection() {
		val cursor: Cursor = contentResolver.query(uri, selection, selectionArgs, sortBy)
		...
	}
	...
}

于是乎,

  1. 获取系统相册、压缩解码或产生缩略图、图片选择状态、等逻辑都可以统一在commonMain中声明为expect;
  2. 在androidMain/iosMain中实现actual;
  3. 在androidApp/iosApp各自编写Recyclerview/UICollectionView
  4. 在androidApp/iosApp调用commonMain提供的expect fetchPhotosCollection方法获取数据(ios实质是调用的shared.framework中的.h文件声明的同名方法)

四、比较

4.1 自身设计思想导致的优缺点

Pros:

  1. 节省开发事件和费用,主要是指的复用业务逻辑这一部分,不用在Android/iOS写两套业务逻辑;
  2. 对已有工程无需重构可以分部分、分步骤接入
  3. 学习成本少,kotlin代码与swift相似,对于iOS开发人员学习也比较容易,与Java完全可相互操作;
  4. 完全使用各自平台的特性,没有额外的性能开销;
  5. 支持双平台调试。

Cons:

  1. 可以shared的编码和通用库、插件比较少;
  2. kotlin multiplatform 工程配置(gradle、plugin主要是)比较繁琐,并且这方面的专家较少;
  3. 目前还处于alpha版本,有许多api还在设计变动阶段,稳定性不足;
  4. 多线程编程不足,kotlin协程iOS完全不支持;
  5. iOS调试方面有许多问题,在多数情况下可能仍然是使用XCode更优。

4.2 与React Native的比较

  1. RN具有完整的生态;
  2. RN在插件、npm库方面具有较大优势;
  3. RN既可以复用一些常用的UI代码,也可以复用部分业务逻辑,并具有双端样式统一的效果;
  4. RN的复用UI需要生成AndroidBundle或JSBundle文件,在双端运行,在Android上增加了额外的性能开销,而KMM各自使用原生特性在性能方面更优;
  5. KMM在业务逻辑和工程架构上具有逻辑清晰,层次分离的效果,对于MVP项目,完全可以把P层、M层全部在shared中实现
  6. 没有桥接这一类概念,在Android中直接调用shared的API,在iOS中也是直接调用shared.framework头文件暴露的接口

五、Demo:

编写了一个获取相册的图片的demo。
基本结构是:
MultiplatGallery:
 |-- shared
 |  |-- commonMain
 |   |-- MLocalMediaManager.kt
 |  |-- androidMain
 |   |-- MLocalMediaManager.kt
 |  |-- iosMain
 |   |-- MLocalMediaManager.kt
 |
 |-- androidApp
 |-- iosApp

expect class MLocalMediaManager() {
    fun fetchMediaCollection(isVideo: Boolean): String?
}

iOSMain

actual fun fetchMediaCollection(isVideo: Boolean): String? {
        var result: String? = null
        val options = PHFetchOptions.alloc()
        options?.apply {
            fetchLimit = 60uL
            predicate = NSPredicate.predicateWithFormat(
                "mediaType == %ld",
                if (isVideo) PHAssetMediaTypeVideo else PHAssetMediaTypeImage)
            val sortDescriptor = NSSortDescriptor
                .sortDescriptorWithKey("creationDate", false)
            sortDescriptors = listOf(sortDescriptor)
        }
        val album = PHAssetCollection.fetchAssetCollectionsWithType(
            PHAssetCollectionTypeAlbum,
            PHAssetCollectionSubtypeAny,
            null
        )
        val albumCollection = PHAssetCollection.fetchTopLevelUserCollectionsWithOptions(null)
        val count = albumCollection.count.toString()
        result = "相册对象:${albumCollection.firstObject.toString()}\n\n 相册个数:${count}个\n"
        if (albumCollection.firstObject !is PHAssetCollection) return result
        val l =
            PHAsset.fetchAssetsInAssetCollection(albumCollection.firstObject as PHAssetCollection, options)
        if (l.count <= 0uL) result += " $l\n\n"
        for (i in 0uL until l.count) {
            result += "图片对象:${l.objectAtIndex(i)}\n\n"
        }
        return result
    }

iOS Swift

import shared

func load() -> String {
    return MLocalMediaManager().fetchMediaCollection(isVideo: false)
}

...
// call load function
struct ContentView: View {
    var body: some View {
        Text(load())
    }
}

在这里插入图片描述

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin Multiplatform 是一种由 JetBrains 开发的跨平台开发框架。它允许开发人员使用 Kotlin 语言编写代码,然后在多个平台上运行,包括 AndroidiOS、Web 等。与传统的跨平台解决方案相比,Kotlin Multiplatform 提供了更高的灵活性和性能。 Kotlin Multiplatform 的核心思想是共享代码。开发人员可以编写一个通用的 Kotlin 模块,其中包含与平台无关的业务逻辑和算法。然后,他们可以根据不同的目标平台,编写平台特定的代码。这样,开发人员可以在不同平台之间共享核心逻辑,减少了重复代码的编写,并且保持了一致性。 Kotlin Multiplatform 目前已经应用于许多项目中。对于 Android 开发人员来说,它提供了更好的性能和开发体验。它允许开发人员在 AndroidiOS 上使用相同的 Kotlin 代码库,从而加快了开发速度和代码复用。对于 iOS 开发人员来说,Kotlin Multiplatform 可以通过共享核心业务逻辑来简化跨平台开发,并且可以与现有的 Objective-C 或 Swift 代码无缝集成。 总之,Kotlin Multiplatform 是一个强大的跨平台开发框架,可以大大简化和提高开发人员的工作效率。它同时适用于 AndroidiOS 开发,并且允许开发人员在不同平台之间共享核心逻辑。在未来,我们可以预见 Kotlin Multiplatform 将会在跨平台开发领域发挥更大的作用,并且有望成为开发人员的首选解决方案。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值