进入KMP

This commit is contained in:
SOR
2026-09-12 21:15:20 +08:00
parent 26ee8636ea
commit 0912708b69
10 changed files with 170 additions and 56 deletions
+1 -13
View File
@@ -96,16 +96,4 @@ kotlin {
}
}
}
```
## 目前仍存在的问题
如果直接在gradle中添加我的依赖,那么,可能会出现这样的问题
```bash
C:\Users\32721\AppData\Local\Temp\ffmpegkit_2621802206663535111.dll: %1 不是有效的 Win32 应用程序。
at java.base/jdk.internal.loader.NativeLibraries.load(Native Method)
......
```
目前,我还不知道如何解决,但可以确认的是,拿编译输出的 jar 包直接用作依赖不会出现这样的问题。
```
+7
View File
@@ -0,0 +1,7 @@
# Kotlin Springboot
> 总体上来讲,Kotlin 与 Java 相比是有好有坏的,优点是写起来更简洁,缺点是写起来更简洁。
## 创建一个 Springboot 项目
这很简单,在 IDEA 中就可以创建,记得选 Kotlin 和 Gradle kts。
View File
-32
View File
@@ -1,32 +0,0 @@
# 项目的基本结构
## 目录
一般来讲,一个 KMP 项目在生成的时候,长这样
- /
- androidApp/
- desktopApp/
- iosApp/
- webApp/ (JS 已经死了,现在的是 WAMS)
- shared/
- src/
- commonMain/
- commonTest/
- androidMain/
- ......
- build.gradle.kts
- .gitignore
- README.md
- gradle 二进制文件(三大平台各一个)
- gradle.proporties
- local.proporties
- build.gradle.kts
- settings.gradle.kts
如果你的项目还要包含 server 的部分的话,那会稍微复杂一点点
## 各部分用途
所有的共享的部分(如UI,基础逻辑)都写在 `shared/commonMain` 里面,不同平台如果要做不同的效果,那么,相应的代码写在对应的 `Main` 里面,`App` 目录主要控制打包设置和进入软件的窗口设定,虽然可以写针对不同平台的不同效果,但是不推荐。
+3 -1
View File
@@ -342,4 +342,6 @@ val a = doOrNull(b) { it.c + 1 }
[目录](../README.md)
[下一篇:深入 Kotlin](../深入Kotlin.md)
[下一篇:深入 Kotlin](./深入Kotlin.md)
[进入 KMP](./进入KMP.md)
+4 -2
View File
@@ -910,10 +910,12 @@ val copied = user.copy(name = "Bob")
println(copied) // User(id=1, name=Bob)
```
`data class` 会自动生成 `toString`、`equals`、`hashCode`、`copy` 等方法,很适合表示简单数据。
`data class` 会自动生成 `toString`、`equals`、`hashCode`、`copy` 等方法,很适合表示简单数据。是一个替代 `Lombook` 的好东西。
## 接下来可以看
[目录](../README.md)
[下一篇:深入 Kotlin](../深入Kotlin.md)
[下一篇:深入 Kotlin](./深入Kotlin.md)
[进入 KMP](./进入KMP.md)
+10
View File
@@ -0,0 +1,10 @@
# 深入 Kotlin
## 接下来可以看
[目录](../README.md)
[下一篇:Kotlin Springboot](./Kotlin-Springboot.md)
[进入 KMP](./进入KMP.md)
+1
View File
@@ -0,0 +1 @@
# 第一个KMP应用
+138 -2
View File
@@ -1,8 +1,144 @@
# 进入KMP
# 进入 KMP
> Kotlin Multiplatform 是基于 Jetpack Compose 框架的多平台应用编译技术,目前支持 Android,所有 JVM 平台,IOS,JS 网页(已被抛弃),WASM 网页。
## 选择一个 IDE
这可能感觉上很怪,但是 **Android Stuidio 对KMP的支持确实是最好的**。
如果你不想做 Android 的部分,用 IDEA 也影响不大,而且 IDEA 有官方中文,Android Stuidio 没有。
如果你不想做 Android 的部分,用 IDEA 也影响不大,而且 IDEA 有官方中文,而 Android Stuidio 没有。
## 项目的基本结构
一般来讲,一个 KMP 项目在生成的时候,长这样
- 项目根目录/
- androidApp/
- desktopApp/
- iosApp/
- webApp/ (JS 已经死了,现在向我们走来的是 WASM)
- shared/
- src/
- commonMain/
- commonTest/
- androidMain/
- ......
- build.gradle.kts
- .gitignore
- README.md
- gradlew (二进制脚本,一个bat,一个sh)
- gradle.proporties
- local.proporties (默认被 ignore)
- build.gradle.kts
- settings.gradle.kts
如果你的项目还要包含 server 的部分的话,那会稍微复杂一点点。
### 各部分用途
所有的共享的部分(如UI,基础逻辑)都写在 `shared/commonMain` 里面,不同平台如果要做不同的效果,那么,相应的代码写在对应的 `Main` 里面,`App` 目录主要控制打包设置和进入软件的窗口设定,虽然可以写针对不同平台的不同效果,但是不推荐。
### 控制依赖与版本
- `build.gradle.kts` 不要动他就好了
- `settings.gradle.kts` 可以设置 Maven 镜像之类
- `shared/build.gradle.kts` 用于控制各个部分会用到的依赖和基本编译选项
- `androidApp/build.gradle.kts` 用于控制 Android 应用编译时的选项
- `desktopApp/build.gradle.kts` 用于控制 desktop 应用编译时的选项
- ......
## 基本的 UI 结构
KMP 的 UI 以 `@Composable` 函数为核心,各个平台的 `main()` 函数入口都会默认调用 `shared/commonMain/kotlin` 中的 `App()` 函数。基本的 UI 也都会在 commonMain 中编写。
### @Composable 函数
我们先讲 `@Composable`,它是一个注解,用于函数或 lambda 变量:
```kotlin
@Composable
fun MyButton(a: Int) {
TODO() // Composable 函数推荐大写首字母
}
val myCard = @Composable { a: Int ->
TODO() // lambda 不推荐
} // 变量类型是 @Composable (Int) -> Unit
```
它有几个重要的特点:
- Composable 函数只能在 main 函数或 Composable 函数中被调用。
- 当 Composable 函数的参数发生变化时,它会被立刻重组(重新触发),受影响的 UI 立刻重新绘制。
- 如果 Composable 函数的 lambda 参数的函数体中出现了 `Compose state` 对象,那么,当它的状态变化时,函数也会被重组。
### 保护变量值
当 Composable 函数被重新触发时,函数中定义的变量值也会重新被定义,这是很不好的,尤其是当我们需要保留变量值的时候。这里,我们有两种方法:
1. 使用 remember 定义变量,使其变成状态对象。函数重组时,新的变量会保留重组前的值。
```kotlin
@Composable
def Counter() {
// 每次重组时,randomId 不会被重新生成,始终保留第一次的值
val randomId = remember { UUID.randomUUID().toString() }
// 如果是可变变量,常使用委托,这里,它是一个枚举类
var currentScreen by remember { mutableStateOf(Screens.Home) }
}
@Composable
def UserProfile(userId: String) {
// 只有当 userId 发生变化时,remember 才会重新计算并更新 userCache
val userCache = remember(userId) {
fetchUserCacheFromDb(userId)
}
}
```
2. 在上一层函数中定义变量,然后把`变量`和`用于修改变量的 lambda 变量`一并传进下一个函数。
```kotlin
// 1. 父级函数:持有状态并定义修改逻辑
@Composable
fun ParentScreen() {
var count by remember { mutableStateOf(0) }
CounterDisplay(
count = count, // 将变量和对应的 Lambda 传入子函数
onIncrement = { count++ } // 用于修改变量的 lambda 变量定义于此
)
}
// 2. 子级函数:无状态组件(Stateless)
@Composable
fun CounterDisplay(
count: Int, // 传入变量值
onIncrement: () -> Unit // 配套的 Lambda
) {
Button(onClick = onIncrement) {
Text("当前点击次数: $count")
}
}
```
### 引发重组
一般来讲就是利用改变参数的值和改变 lambda 中的 Compose state 状态来触发。利用参数变化并没有很常见。
```kotlin
@Composable
def Counter() {
// 使用 remember + mutableStateOf 保持状态并在值变化时触发重组
var count by remember { mutableStateOf(0) }
Button(onClick = { count++ }) {
Text("点击次数: $count")
}
}
```
## 接下来可以看
[目录](../README.md)
[下一篇:第一个 KMP 应用](./第一个KMP应用.md)
+6 -6
View File
@@ -5,12 +5,14 @@
### 引入 Kotlin
1. [引入 Kotlin](Learning/引入Kotlin.md)
2.
2. [深入 Kotlin](Learning/深入Kotlin.md)
3. [Kotlin Springboot](Learning/Kotlin-Springboot.md)
4.
### Kotlin Multiplatform
1. [基本的项目结构](Learning/基本的项目结构.md)
2. [基本的 UI 结构](Learning/基本的UI结构.md)
1. [进入 KMP](learning/进入KMP.md)
2. [第一个 KMP 应用](Learning/第一个KMP应用.md)
3.
### 番外
@@ -20,6 +22,4 @@
## 有关 KMP 的项目结构
KMP的项目结构一直在变化,但好在公用代码的目录一直是`commonMain`,如果没有特别的说明,那么我就是在说`shared/src/commonMain`的事。
KMP 的项目结构一直在变化,但好在公用代码的目录一直是 `commonMain`,如果没有特别的说明,那么我就是在说 `shared/src/commonMain` 的事。