kmm的初次接触

什么是KMM?

KMM 即 Kotlin Multiplatform Mobile 是一个 SDK,旨在简化跨平台移动应用程序的开发。通过 KMM 开发者可以在 iOS 和 Android 应用程序之间共享通用代码,并仅在必要时编写特定于平台的代码。 

KMM并不会替代 Android 和 iOS 的原生开发, 而是提倡将共有的逻辑部分抽出,由 KMM 封装成 Android(Kotlin/JVM) 的 aar 和 iOS(Kotlin/Native) 的 framework ,再提供给 View 层进行调用,从而节约一部分的工作量。

KMM 的优势

  1. 无需内置多套引擎(runtime),包体积增量更少 。

  2. 对于 Android 开发者无需多学习一套编程语言和编程思想,门槛更低 。

  3. 基于双端标准组件输出,审核被拒风险较小(iOS)。

  4. 更强的互操作性, 支持与本地编程语言的双向互操作,可以直接使用现有库,避免了众多基础组件的重复建设。

环境配置

这里假设,你有 android 开发基础并且已经安装了高版本的 AndroidStudio(这里不会介绍 Xcode 的配置)。

在 AndroidStudio 中搜索插件 Kotlin Multiplatform Mobile 并安装,如下图所示:(我这边是已经装好了)

创建项目

安装好插件并重启后,我们可以创建一个 Kotlin Multiplatform App,如下图所示:

项目结构

Kotlin Multiplatform App(左)、   Kotlin Multiplatform Libray(右)

androidApp、iosApp 就是对应的 Android、iOS 代码库,shared 为共享逻辑模块,即存放 Android、iOS 公共业务逻辑的部分。

我们看看根目录的settings.gradle.kts文件都做了什么:

pluginManagement {
    repositories {
        google()
        gradlePluginPortal()
        mavenCentral()
    }
}

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
}

rootProject.name = "My_Application"
include(":androidApp")
include(":shared")

主项目只 include 了 androidApp 和 shared 这两个子项目,因为这两个项目是Gradle项目,而 iOS 作为 Xcode 项目,储存在根项目的另一个文件夹。Xcode 有自己的编译系统,因此 iOS 项目并不依靠 Gradle 去和共享工程建立联系,而是依靠将共享工程打包成 framework 供 iOS 项目使用。

 

commonMain 为公共模块,该模块的代码与平台无关,是通过 expecteed 关键字对一些 api 的声明(声明的实现在 platfrom module 中)。

androidMain 和 iosMain 分别为Android 和 ios 相关的生态系统常规库,通过 actual 关键字在平台模块进行具体实现。

我们继续看看shared模块的gradle文件都做了什么:

plugins {
    kotlin("multiplatform")//意味着引入KMM 插件。
    id("com.android.library")//意味着生成一个Android aar,其配置用android {}进行了包裹
}

@OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
kotlin {
    targetHierarchy.default()

    android {
        compilations.all {
            kotlinOptions {
                jvmTarget = "1.8"
            }
        }
    }

    //iOS framework是使用Kotlin/Native进行编译的,相应的配置是用iosXXX{}进行了包裹
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        //定义了输出格式为framework,输出名称为shared。
        it.binaries.framework {
            baseName = "shared"
        }
    }

    //支持分别引入implementation来实现各自的逻辑。另外Kotlin标准库会被自动加到相应的sourceSet中,无需重复引入。
    sourceSets {
        val commonMain by getting {
            dependencies {
                //put your multiplatform dependencies here
//              implementation("io.ktor:ktor-client-darwin:$ktorVersion")
            }
        }
        val androidMain by getting {
            dependencies {

            }
        }
        val iosMain by getting {
            dependencies {

            }
        }
        val commonTest by getting {
            dependencies {
                implementation(kotlin("test"))
            }
        }
    }
}

android {
    namespace = "com.example.myapplication"
    compileSdk = 33
    defaultConfig {
        minSdk = 24
    }
}

运行程序

我们这里仅运行Android程序,运行结果如下图所示:

这个结果来自 shared 模块中 commonMain 下的 Greeting 文件的 greet 方法,代码如下所示:

class Greeting {
    private val platform: Platform = getPlatform()

    fun greet(): String {
        return "Hello, ${platform.name}!"
    }
}

greet() 方法调用 platform.name, Platform 的实现如下:( Platform 是个接口,使用expect 关键字来声明 getPlatform(),再由 Android 和 iOS 通过使用 actual 关键字分别实现)

interface Platform {
    val name: String
}

expect fun getPlatform(): Platform

这个 platform.name 的值在 android 手机调用时会取 androidMain 下的 Platform.android.kt 中的值,所以取出的是 android 手机的sdk版本,代码如下所示:

class AndroidPlatform : Platform {
    override val name: String = "Android ${android.os.Build.VERSION.SDK_INT}"
}

actual fun getPlatform(): Platform = AndroidPlatform()

同一个方法在iOS的手机上运行则会显示iOS版本号,因为取的是 iosMain 下的  Platform.ios.kt 中的值,代码如下所示:

class IOSPlatform: Platform {
    override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}

actual fun getPlatform(): Platform = IOSPlatform()

最后由 androidApp和iosApp用各自的调用。这里只说 android 端。androidApp 下的 MainActivity.kt 中使用 Greeting.greet() 方法获取然后显示,代码如下所示:

至于为什么 activity 的写法和之前差距很大,那是因为这里默认使用的是 kotlin+compose 的写法,一种不需要xml的布局写法...这里就先不细说了。

expect/actual 机制 

KMM 里 expect/actual 机制是非常重要的,因为它提供了一种语法技术来解决平台相关的问题。举例来说,当我们需要在业务逻辑中使用设备的 model 号时,我们需要编写特定平台的代码才能实现。这时,expect 就相当于声明一个协议,它定义了我们期望得到的接口或数据,之后各个平台都需要独立地实现这个协议来满足业务需要。

基本流程如下: 在 commonMain 目录下建立一个 expect 类或 Top-Level 方法 , 类似创建一个协议声明。分别在 androidMain 和 iosMain 目录中,创建与 expect 声明(类名、方法名、参数类型及名称)完全一致的实现,否则编译器会报错。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值