回答

收藏

用 KMM(kotlin)创建Android和iOS跨平台应用 下

Kotlin Kotlin 10164 人阅读 | 0 人回复 | 2020-09-27

*本帖最后由 LazyGirl 于 2020-10-19 08:27 编辑*

由于篇幅限制,请看上部分:[https://www.harm-os.com/thread-6-1-1.html](https://www.harm-os.com/thread-6-1-1.html) 用 KMM(kotlin)创建Android和iOS跨平台应用 上


## 共享模块
共享模块包含 Android 和 iOS 应用程序通用的代码。然而,为了在 Android 和 iOS 上实现同样的逻辑,有时候你需要编写两个平台特定的版本,比如蓝牙、 Wifi。为了处理这种情况,Kotlin 提供了预期/实际机制。共享模块的源代码相应地组织成三个源集:
1_7xj04EcQBIT4XWr1FpfqPw.png


- commonMain stores the code that works on both platforms, including the 存储在两个平台上工作的代码,包括expect declarations 声明
- androidMain stores Android-specific parts, including 存储特定于 android 的部件,包括actual implementations 实现
- iosMain stores iOS-specific parts, including 存储特定于 ios 的部件,包括actual implementations 实现

每个源都有自己的依赖项。Kotlin 标准库自动添加到所有源代码集中,您不需要在构建脚本中声明它。

**build.gradle.kts**
build.gradle.kts 拥有 Android 和 IOS 的共享模块配置:
```java
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget

plugins {
    kotlin("multiplatform")
    id("com.android.library")
    id("kotlin-android-extensions")
}
group = "com.aman.helloworldkmm"
version = "1.0-SNAPSHOT"

repositories {
    gradlePluginPortal()
    google()
    jcenter()
    mavenCentral()
}
kotlin {
    android()
    ios {
        binaries {
            framework {
                baseName = "shared"
            }
        }
    }
    sourceSets {
        val commonMain by getting
        val commonTest by getting {
            dependencies {
                implementation(kotlin("test-common"))
                implementation(kotlin("test-annotations-common"))
            }
        }
        val androidMain by getting {
            dependencies {
                implementation("com.google.android.material:material:1.2.0")
            }
        }
        val androidTest by getting {
            dependencies {
                implementation(kotlin("test-junit"))
                implementation("junit:junit:4.12")
            }
        }
        val iosMain by getting
        val iosTest by getting
    }
}
android {
    compileSdkVersion(29)
    sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
    defaultConfig {
        minSdkVersion(24)
        targetSdkVersion(29)
        versionCode = 1
        versionName = "1.0"
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false
        }
    }
}
val packForXcode by tasks.creating(Sync::class) {
    group = "build"
    val mode = System.getenv("CONFIGURATION") ?: "DEBUG"
    val sdkName = System.getenv("SDK_NAME") ?: "iphonesimulator"
    val targetName = "ios" + if (sdkName.startsWith("iphoneos")) "Arm64" else "X64"
    val framework = kotlin.targets.getByName<KotlinNativeTarget>(targetName).binaries.getFramework(mode)
    inputs.property("mode", mode)
    dependsOn(framework.linkTask)
    val targetDir = File(buildDir, "xcode-frameworks")
    from({ framework.outputDirectory })
    into(targetDir)
}
tasks.getByName("build").dependsOn(packForXcode)
```
> AndroidApp: build.gradle.kts

```kotlin
plugins {
    id("com.android.application")
    kotlin("android")
    id("kotlin-android-extensions")
}
group = "com.aman.helloworldkmm"
version = "1.0-SNAPSHOT"

repositories {
    gradlePluginPortal()
    google()
    jcenter()
    mavenCentral()
}
dependencies {
    implementation(project(":shared"))
    implementation("com.google.android.material:material:1.2.0")
    implementation("androidx.appcompat:appcompat:1.2.0")
    implementation("androidx.constraintlayout:constraintlayout:1.1.3")
}
android {
    compileSdkVersion(29)
    defaultConfig {
        applicationId = "com.aman.helloworldkmm.androidApp"
        minSdkVersion(24)
        targetSdkVersion(29)
        versionCode = 1
        versionName = "1.0"
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false
        }
    }
}
```

**使用 Expect/Actual 关键字**
对于多平台应用程序来说,具有特定于平台的代码是很常见的。例如,你可能想知道你的应用程序是运行在 Android 还是 iOS 设备上,然后得到你的设备的确切型号。要实现这一点,您需要使用**expect/actual**的关键字。


首先,使用 expect 关键字在通用模块中声明类、方法或函数,并像创建接口或抽象类时经常做的那样保持主体为空。然后,使用签名中的实际关键字在所有其他模块中编写类、方法或函数的特定于平台的实现。
1_dDC1DXjLsmFXQUa-DYiPMA.png

注意: 如果您在包中使用 expect,那么您还需要在具有相同名称的包中使用相应的实际命令。
否则就会出现这个错误:
1_yNTSu1B7G2A297JgA0vn9A.png


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


```kotlin
import platform.UIKit.UIDevice


actual class Platform {
    actual val platform: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}
```
MainActivity.kt (Android):
```kotlin
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val tv: TextView = findViewById(R.id.text_view)
        tv.text = "Hello World, ${Platform().platform}!"
    }
}
```
ContentView.swift (iOS)
```kotlin
struct ContentView: View {
    var body: some View {
        Text("Hello World, "+ Platform().platform)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
```
恭喜你! ! 你已经开发了你的第一个 KMM 应用。




分享到:
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则