KMM初探

什么是KMM?

在开始使用 KMM 之前,您需要了解 Kotlin。

KMM 全称:Kotlin Multiplatform Mobile)是一个用于跨平台移动开发的 SDK,相比于其他跨平台框架,KMM是原生UI+逻辑共享的理念,由KMM封装成Android(Kotlin/JVM)的aar和iOS(Kotlin/Native)的framework,再提供给View层进行调用。

a45a47064c5aab78124a5e9c53a807bf.jpeg

Kotlin Multiplatform Mobile

KMM使用Kotlin 的多平台功能,您可以使用 Kotlin 语言和技术栈,开发一套可以在多平台之间共享的代码库,用来构建统一的代码逻辑,而不用针对各个平台都去实现自己的一套。

0cd32194d2a9b8be6457fbe39b37ea20.png

Kotlin多平台功能

环境工具准备

为了能够开始使用 KMM,您应该安装以下内容:

  1. Java JDK 版本 11

  2. Ruby and Bundler

  3. Android Studio — 版本 4.2 或更高版本

  4. Xcode — 版本 11.3 或更高版本

  5. Xcode 命令行开发者工具

  6. 适用于 Android Studio 的Kotlin Multiplatform Mobile plugin。在 Android Studio 中,选择 Preferences > Plugins > 在 Marketplace 中搜索插件Kotlin Multiplatform Mobile并安装:

b5cfc9c2bb5da796751fd5890c6e627e.png

kmm plugin

准备就绪,是时候创建您的第一个 KMM 项目了

创建第一个 KMM 项目

使用以下步骤创建您的第一个 KMM 项目:

1、打开Android Studio并点击新建 项目:

95751660f568e7d89d5a1085c4c16a92.png

2、在项目的模板中,向下滚动,您将看到我们现在有了KMM 应用程序。选择它并单击下一步按钮:

1874c9a4cf5af139a558b2db7f3280de.png

3、输入您的项目名称、要使用的最低 SDK,然后单击“下一步”按钮:

5fd471d7cd762217bedcf1dc97927bd5.png

4、更改 Android 和 iOS 的应用程序名称。您还可以更改共享模块名称,这里使用默认值并选择为shared模块添加示例测试:

3eb40819153ddbbc1329a336d68e04cc.png

对于iOS应用程序,可以在regular framework或CocoPods依赖管理器之间切换依赖关系,在环境工具准备中我提到需要为CocoPods安装Ruby 。

  1. 单击“下一步”按钮并等待 Gradle 完成项目设置:

项目加载成功后,现在可以找到项目中所有的app模块:

  • androidApp

  • iosApp

  • shared

3d6dfbc00c1b805e8afe6d2ae447df95.png

image
  1. 配置中选择androidApp 和 iOSApp运行到相应模拟器


434d9c0704e2ede140fc141893745e71.png

image

通过AndroidStudio就可运行iOS,通常使用 Xcode 来开发 iOS 原生应用

KMM 会利用 Gradle 插件,自动调用 Xcode 进行构建,并调起 iOS 模拟器运行

9b965f63f35c13c48a671d3ffc79b35b.png

image

注意:使用 KMM Plugin 建立的工程,会默认使用 Kotlin(.kts 文件)的形式来进行 Gradle 配置,另外,其新建的 iOS 工程,也默认使用 Swift UI 进行开发,且这两项不可以配置,如果需要使用传统的 iOS UI 开发形式,需要以集成的形式来新建 KMM Module

KMM工程结构

KMM 插件建立的 KMM 工程的文件目录结构说明

├── androidApp        # 实际 Android APP Module
├── build.gradle.kts  # 工程根 Gradle 配置
├── gradle
├── gradle.properties
├── gradlew
├── gradlew.bat
├── iosApp            # 实际的 iOS 工程根目录
├── local.properties
├── settings.gradle.kts
└── shared            # KMM 模块代码目录├── build.gradle.kts  # KMM 模块 Gradle 配置(依赖、插件、构建 Task、cinterop 等配置)└── src # 内部模块形式都为 Gradle 工程 Module├── androidMain    # Android 差异化代码,最终生成 AAR├── commonMain     # 共享模块 API 代码├── iosMain        # iOS 差异化代码,└── nativeInterop  # 默认不会创建,用来存放 *.def 文件,描述与 C/C++ 代码,或 Apple Framework 交互时,构建 klib 的配置

b3c05c2a2eb68fd4787636a5ac1c39b2.jpeg

image

androidApp和iosApp为Android和iOS这两个平台的工程模块,shared为共享逻辑模块,供androidApp和iosApp调用。

打开根目录的settings.gradle.kts:

pluginManagement {repositories {google()gradlePluginPortal()mavenCentral()}
}dependencyResolutionManagement {repositories {google()mavenCentral()}
}rootProject.name = "wb-kmm-demo"
include(":androidApp")
include(":shared")

会发现主项目只include了androidApp和shared这两个子项目,因为这两个项目是Gradle项目

iOS的项目如何引用呢?

The iOS application is produced from an Xcode project. It's stored in a separate directory within the root project. 
Xcode uses its own build system; thus, the iOS application project isn't connected with other parts of the Multiplatform Mobile project via Gradle. 
Instead, it uses the shared module as an external artifact – framework.

iOS作为Xcode项目,储存在根项目的另一个文件夹。Xcode有自己的编译系统,因此iOS项目并不依靠Gradle去和shared工程建立联系,而是依靠将shared工程打包成framework供iOS项目使用。我们可以看一下shared模块编译后的产物,如下图所示:

f358182a5964ebd699184c88264df427.png

image

特定于平台的API和实现

因为在我们的核心目标即双端共享代码。

什么是共享代码呢?让同一份代码能在 Android & iOS 上运行。

怎么实现这个目标呢?简单来说把全部代码分为两个部分,其中一部分就是与平台无关的代码。

啥是平台无关的代码呢?比如我们要编写一个检验手机号的算法,我们认为这个算法的代码是平台无关的,因为输入就是一个字符串,里面的实现就是根据指定的规则来判断这个输入字符串的合法性,期间不涉及任何平台相关特性的访问,比如系统 API 的访问。

但是光有与平台无关的代码是不够的,一旦涉及到与平台相关的访问,例如获取设备版本号或硬件信息等,我们就需要独立于平台去完成它们,这些独立于平台的代码在哪实现?这就有了 AndroidApp 和 iOSApp 这两个模块。在“shared”模块中,我们包含了核心应用程序逻辑,例如类、函数,并使用 Gradle 作为构建系统。

expect/actual 机制 :

ad6a7e7cf10fafbfaa569e84d68454b9.jpeg

image

KMM 里 expect/actual 机制是非常重要的,因为它提供了一种语法技术来解决平台相关的问题。

拿工程例子来说,工程逻辑中使用设备的 版本 号时,这里需要编写特定平台的代码才能实现。这时,expect 就相当于声明一个协议,它定义了我们期望得到的接口或数据,之后各个平台都需要独立地实现这个协议来满足业务需要。

shared模块的实现形式

进入shared模块,看commonMain文件夹下Greeting的实现:

class Greeting {private val platform: Platform = getPlatform()fun greet(): String {return "Hello, ${platform.name}!"}
}

greeting()方法调用platform.name,Platform的实现如下:

interface Platform {val name: String
}expect fun getPlatform(): Platform

Platform是个接口,使用expect关键字来声明getPlatform(),再由Android和iOS通过使用actual关键字分别实现:

Android:

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

iOS:

import platform.UIKit.UIDeviceclass IOSPlatform: Platform {override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}actual fun getPlatform(): Platform = IOSPlatform()

最后是两端调用实现:

Android:

class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MyApplicationTheme {Surface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colors.background) {GreetingView(Greeting().greet())}}}}
}@Composable
fun GreetingView(text: String) {Text(text = text)
}

iOS:

import SwiftUI
import sharedstruct ContentView: View {let greet = Greeting().greet()var body: some View {Text(greet)}
}struct ContentView_Previews: PreviewProvider {static var previews: some View {ContentView()}
}
KMM vs Flutter vs RN

与 Flutter 这种框架的思想相反,KMM 是用一套语言生成多个平台特定的字节码,所有的翻译工作由 kotlinc 或 kotlin-nativec 编译器来执行,从某种角度来讲,是『从上到下』;

而 Flutter 的思想是『从下到上』,这也决定了两种框架适用的场景,Flutter 就适合绘制 UI,而 KMM 则是更适合与 UI 无关的逻辑代码,比如:Model 层,网络请求、数据解析、建模等

  • 体积:

  1. 使用 Flutter 需要在 App 包内部增加两个引擎:

  • 一个是 Flutter 的渲染引擎,该引擎使用 C/C++ 开发,直接调用 OpenGL/Skia 的 API 进行绘制,从而摆脱 iOS 的 UIKit 以及 Android 的 View 组件直接渲染成需要的样式,保证样式高度统一

  • 另一个是 Dart 语言的 Runtime,用于解析并运行 Dart 语言编译的 Bundle

这两者减小了开发者的适配成本,但增大了 APP 的包体积(其他类似的跨平台框架,如:React Native 等,也会内置 JavaScript Core 或 V8 引擎)

  1. KMM 针对不同平台生成不同的二进制依赖包,根本上还是调用了 Android、iOS 的原生 API,并不会内置引擎这类文件,对 App 的体积影响相对较小

  • 技术栈:

    • Flutter-->Dart

    • KMM-->Kotlin

    • RN-->JavaScript

  • 适用场景:

    • 由于 Flutter 采用类似 3D 游戏的渲染理念,统一了界面渲染引擎,利用 Dart 可以高度保证双端样式和交互逻辑一致,且几乎不存在界面适配问题,完全抹平了 TextView 和 UILabel 这类控件之间的差异,所以 Flutter 适合于界面构建

    • 而 KMM 并不适合 UI,双端的组件,生命周期、API 差异都比较大,KMM 在技术上可以实现功能,但相当于写了两份代码,失去了意义

Flutter和RN这两者虽然在设计及原理上区别很大,但设计思想上都是采用非原生开发语言在 Android 与 iOS 系统框架之上搭建的“阁楼”上运行,每个采用这些框架的 App 在打包时需要集成语言的 Runtime、框架的底层组件等许多重量级的包与库。并且 JavaScript 或 Dart 与原生开发语言(Java/Kotlin、Objective-C/Swift)之间的交互需要通过“桥接通讯”实现

KMM提出了不同于 RN 与 Flutter 的跨端解决方案,即使用不同的编译器编译同一份代码生成各端的不同产物来达到跨平台的目的,这就是 Kotlin Multiplatform

Kotlin Multiplatform

Kotlin Multiplatform 技术可为多种平台创建应用程序并在平台之间高效重用代码,同时保留原生编程的优势。多平台应用程序将在不同操作系统上运行,例如 iOS、Android、macOS、Windows 和 Linux 等。

27c016ec58560938bb22782af63cddff.png

image

Kotlin 官方开发的多平台框架分为以下几种:

  • 标准 Kotlin(Kotlin Stdlib)

    • 即 Kotlin JVM,由于 Kotlin 最初是基于 JVM 运行的,所以可以使用 Java  的全部功能

  • Kotlin Native

    • 简称 KN,内部对各平台(如:Windows、Linux、macOS、iOS、Android)Native API(不使用 Runtime 的)进行封装,底层使用 LLVM 进行编译,可以使用 Kotlin 调用各平台特定的 API 或基于一些 C/C++ 库的能力,如:OpenGL、OpenCV 等,使多平台共享一套 Kotlin 代码

  • Kotlin JS

    • 基本原理是将 Kotlin 代码翻译成 JavaScript,同时可以调用一些 JavaScript 特定的接口,从而进行 Web 开发

f8f6aa80a24457ff4eb23d5b28dae684.png

image

Compose Multiplatform,一个由JetBrains开发的基于kotlin的声明式UI框架,实现了在Android和iOS之间共享UI,实现了完全跨平台的应用程序:

c7a29c3acbe6d6ad62ba9c6752003e64.jpeg

image

Kotlin Multiplatform Wizard

我们可以使用Kotlin Multiplatform Wizard创建跨平台应用程序。

f847df6b83880cc09ab12265817c86f2.png

image

这里可以选择运行它的平台,如果我们选择与compose多平台共享UI为iOS项目,我们可以创建UI与compose多平台,并运行与iOS和android不使用Swift UI。我们也可以用它创建服务器和桌面项目。

10dea5514932dd13f9939e574a36861a.png


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/757331.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

AI大模型智能大气科学探索之:ChatGPT在大气科学领域建模、数据分析、可视化与资源评估中的高效应用及论文写作

本文深度探讨人工智能在大气科学中的应用,特别是如何结合最新AI模型与Python技术处理和分析气候数据。课程介绍包括GPT-4等先进AI工具,旨在帮助大家掌握这些工具的功能及应用范围。本文内容覆盖使用GPT处理数据、生成论文摘要、文献综述、技术方法分析等…

Http的缓存有哪些

HTTP 缓存可以通过多种 HTTP 头部字段来控制,主要包括以下几种: 1.Expires:这个字段定义了响应的过期时间。如果当前时间小于 Expires 的时间,那么就可以直接使用缓存。 2.Cache-Control:这个字段是一个指令&#xff…

Java 学习和实践笔记(41):API 文档以及String类的常用方法

JDK 8用到的全部类的文档在这里下载: Java Development Kit 8 文档 | Oracle 中国

docker入门(一)—— docker概述

docker 概述 docker 官网:http://www.docker.com 官网文档: https://docs.docker.com/get-docker/ Docker Hub官网:https://hub.docker.com (仓库) 什么是 docker docker 是一个开源的容器化平台,可以…

C语言经典面试题目(十六)

1、什么是C语言中的指针常量和指针变量?它们有什么区别? 在C语言中,指针常量和指针变量是指针的两种不同类型。它们的区别在于指针的指向和指针本身是否可以被修改。 指针常量:指针指向的内存地址不可变,但指针本身的…

FSP40罗德与施瓦茨FSP40频谱分析仪

181/2461/8938产品概述: 频率范围:9千赫至40千兆赫 分辨率带宽:1赫兹至10兆赫 显示的平均噪音水平:-155分贝(1赫兹) 相位噪声:10 kHz时为-113 dB(1Hz) 附加滤波器:100 Hz至5 MHz的通道滤波器和RRC滤波器、1 Hz至3…

数据仓库系列总结

一、数据仓库架构 1、数据仓库的概念 数据仓库(Data Warehouse)是一个面向主题的、集成的、相对稳定的、反映历史变化的数据集合,用于支持管理决策。 数据仓库通常包含多个来源的数据,这些数据按照主题进行组织和存储&#x…

在Qt中使用线程类QThread

说明 QThread是qt中的一个线程类。目前我了解到的共有两种用法,一种是作为普通的线程,就像c标准库中的std::thread一样,另一种就是作为信号槽的容器,负责调用qt的事件循环。 作为普通线程 重载QThread::run()这个虚函数&#x…

深度学习基础之《TensorFlow框架(7)—变量》

一、什么是变量 1、TensorFlow变量是表示程序处理的共享持久状态的最佳方法。变量通过tf.Variable OP类进行操作 这里的变量和传统认知里存储值或者返回值不一样,他是TensorFlow里的一个组件 2、变量的特点 (1)存储持久化 把程序中定义的数…

Springboot+vue的仓库管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频: Springbootvue的仓库管理系统(有报告)。Javaee项目,springboot vue前后端分离项目。 项目介绍: 采用M(model)V(view)C(controller)三层…

Leetcode 62. 不同路径

心路历程: 这道题基本就是Q-learning经典迷宫问题的简化版本,所以肯定是用动态规划了,毕竟RL中的时序差分估计法的本身也是来自于MC和动态规划的结合。如果正常正向思维思考的话,首先看不到问题明显的循环结构,考虑递…

秒级生图,大模型 SDXL-turbo、LCM-SDXL 实战案例来了

最近一个月,快速生图成为文生图领域的热点,其中比较典型的两种方式的代表模型分别为SDXL-turbo 和 LCM-SDXL。 SDXL-turbo 模型是 SDXL 1.0 的蒸馏版本,SDXL-Turbo 基于一种称之为对抗扩散蒸馏(ADD)的新颖的训练方法&…

Go 1.22 - 更加强大的 Go 执行跟踪

原文:Michael Knyszek - 2024.03.14 runtime/trace 包含了一款强大的工具,用于理解和排查 Go 程序。这个功能可以生成一段时间内每个 goroutine 的执行追踪。然后,你可以使用 go tool trace 命令(或者优秀的开源工具 gotraceui&a…

c++11 标准模板(STL)本地化库 - std::iscntrl(std::locale) 检查字符是否被本地环境分类为控制字符

本地化库 本地环境设施包含字符分类和字符串校对、数值、货币及日期/时间格式化和分析,以及消息取得的国际化支持。本地环境设置控制流 I/O 、正则表达式库和 C 标准库的其他组件的行为。 检查字符是否被本地环境分类为控制字符 std::iscntrl(std::locale) templa…

Spring Cloud 整合 GateWay

目录 第一章 微服务架构图 第二章 Spring Cloud整合Nacos集群 第三章 Spring Cloud GateWay 第四章 Spring Cloud Alibaba 整合Sentinel 第五章 Spring Cloud Alibaba 整合SkyWalking链路跟踪 第六章 Spring Cloud Alibaba 整合Seata分布式事务 第七章 Spring Cloud 集成Auth用…

[Qt学习笔记]Release后的exe程序在新的电脑上出现“找不到MSVCP140.dll”的错误

1、背景介绍 我们在打包程序的时候一般都会把相关依赖库整体打包,这样程序在新的电脑和环境下就不需要再去配置对应的环境,但是有时候新程序在一台新的电脑运行时会出现“找不到MSVCP140.dll”这种错误,其原因就是在新电脑的操作系统中缺少一…

倒计时 7 天 | 立即加入 GDE 成长计划,飞跃成为谷歌开发者专家

谷歌开发者专家 (Google Developer Experts,GDE),又称谷歌开发者专家项目,是由一群经验丰富的技术专家、具有社交影响力的开发者和思想领袖组成的全球性社区。通过在各项活动演讲以及各个平台上发布优质内容来积极助力开发者、企业和技术社区…

机器学习 - 线性问题

矩阵做 transpose import torch tensor_Matrix_A torch.tensor([[1,2],[4,5],[7,8] ], dtypetorch.float32) print(tensor_Matrix_A.T)# 结果 tensor([[1., 4., 7.],[2., 5., 8.]])torch.nn.Linear() 模块也被称为 “feed-forward layer"或者"fully connected laye…

如何防御XSS攻击

上篇讲解了XSS的危害,name在开发网站时就要做好防御措施,具体措施如下: 可以从浏览器的执行来进行预防,一种是使用纯前端的方式,不用服务器端拼接后返回(不适用服务器端渲染)。另一种是对需要插…

安卓面试题多线程 91-95

91. 简述java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?有两种实现方法,分别是继承Thread类与实现Runnable接口 用synchronized关键字修饰同步方法 反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而…