从0到1搭建KMP项目:让你的移动开发效率提升100%

从0到1搭建KMP项目:让你的移动开发效率提升100%

项目概述

本项目是一个 Kotlin Multiplatform (KMP) 示例工程,展示了 Kotlin 跨平台编译的底层机制。与普通 Android 项目不同,KMP 项目通过 Kotlin/NativeCommonizer 技术实现真正的跨平台代码共享,将 Kotlin 代码编译为多个平台的原生代码。

KMP vs 普通 Android 项目的本质区别

1. 编译目标差异

普通 Android 项目:

  • 只编译为 JVM 字节码 (.class 文件)
  • 运行在 Android Runtime (ART) 上
  • 构建产物:APK/AAB 文件

KMP 项目:

  • 同时编译为多个目标平台的原生代码
  • Android: JVM 字节码
  • iOS: LLVM IR → 原生机器码
  • 构建产物:Android AAR + iOS Framework

2. 关键目录结构对比

普通 Android 项目:
app/
├── build/
│   ├── intermediates/          # Android 编译中间产物
│   └── outputs/               # APK 输出
└── src/main/kotlin/           # 单一平台代码

KMP 项目:
shared/
├── build/
│   ├── bin/                   # 🔥 多平台二进制文件
│   │   ├── iosArm64/          # iOS 真机 Framework
│   │   ├── iosSimulatorArm64/ # iOS 模拟器 Framework  
│   │   └── iosX64/            # iOS x86_64 模拟器 Framework (Intel Mac)
│   ├── classes/kotlin/        # 多平台编译产物
│   │   ├── commonizer/        # Commonizer 处理结果
│   │   ├── android/           # Android JVM 字节码
│   │   │   └── main/          # Android 主模块编译产物
│   │   ├── iosArm64/          # iOS ARM64 KLIB
│   │   ├── iosSimulatorArm64/ # iOS 模拟器 ARM64 KLIB
│   │   ├── iosX64/            # iOS x64 KLIB
│   │   └── metadata/          # 平台元数据
│   ├── libs/                  # 🔥 Android 库文件
│   │   └── shared-debug.aar   # Android AAR 包
│   ├── outputs/               # Android 输出产物
│   │   └── aar/               # AAR 文件目录
│   │       ├── shared-debug.aar
│   │       └── shared-release.aar
│   └── fat-framework/         # 通用 iOS Framework
└── src/
    ├── commonMain/            # 跨平台共享代码
    ├── androidMain/           # Android 特定实现
    └── iosMain/              # iOS 特定实现

项目结构详解

KMPDemo/
├── androidApp/                 # Android 应用模块
│   ├── build.gradle.kts        # Android 构建配置
│   └── src/main/
│       ├── AndroidManifest.xml # Android 清单文件
│       └── java/com/example/kmpdemo/androidApp/
│           └── MainActivity.kt  # Android 主活动
├── shared/                     # 共享模块
│   ├── build.gradle.kts        # 共享模块构建配置
│   └── src/
│       ├── androidMain/        # Android 特定代码
│       ├── commonMain/         # 跨平台共享代码
│       │   └── kotlin/
│       │       ├── Greeting.kt # 问候功能
│       │       └── Platform.kt # 平台信息
│       └── iosMain/            # iOS 特定代码
├── build.gradle.kts            # 根项目构建配置
├── settings.gradle.kts         # 项目设置
└── gradle/                     # Gradle 配置
    └── libs.versions.toml      # 版本目录

编译结果详解

1. 编译产物总览

KMP 项目编译后会在 shared/build/ 目录下生成多种类型的产物:

shared/build/
├── bin/                        # 🔥 多平台二进制文件
│   ├── iosArm64/
│   │   └── debugFramework/
│   │       └── shared.framework    # iOS 真机 Framework
│   ├── iosSimulatorArm64/
│   │   └── debugFramework/
│   │       └── shared.framework    # M1 Mac 模拟器 Framework
│   └── iosX64/
│       └── debugFramework/
│           └── shared.framework    # Intel Mac 模拟器 Framework
├── classes/kotlin/             # 多平台编译中间产物
│   ├── android/
│   │   └── main/               # Android JVM 字节码 (.class)
│   ├── iosArm64/
│   │   └── main/klib/
│   │       └── shared.klib     # iOS ARM64 KLIB 文件
│   ├── iosSimulatorArm64/
│   │   └── main/klib/
│   │       └── shared.klib     # iOS 模拟器 ARM64 KLIB
│   ├── iosX64/
│   │   └── main/klib/
│   │       └── shared.klib     # iOS x64 KLIB 文件
│   ├── commonizer/             # Commonizer 处理结果
│   └── metadata/               # 平台元数据
│       ├── commonMain/
│       └── iosMain/
├── libs/                       # Android 库文件
│   ├── shared-debug.aar        # Android Debug AAR 包
│   └── shared-release.aar      # Android Release AAR 包
├── outputs/aar/                # Android 输出产物
│   ├── shared-debug.aar
│   └── shared-release.aar
├── fat-framework/              # 通用 iOS Framework
│   ├── debug/
│   │   └── shared.framework    # 包含所有架构的 Framework
│   └── release/
│       └── shared.framework
└── XCFrameworks/               # 🔥 Xcode 通用 Framework
    ├── debug/
    │   └── shared.xcframework
    └── release/
        └── shared.xcframework

2. Android 编译产物详解

2.1 AAR 包结构
shared-debug.aar
├── AndroidManifest.xml         # Android 清单
├── classes.jar                 # 编译后的 Java/Kotlin 字节码
├── R.txt                       # 资源 ID 映射
├── res/                        # Android 资源文件
└── META-INF/                   # 元数据信息
2.2 JVM 字节码
shared/build/classes/kotlin/android/main/
├── com/example/kmpdemo/
│   ├── Greeting.class          # 编译后的 Kotlin 类
│   ├── Platform.class
│   └── GreetingKt.class        # 顶级函数编译结果
└── META-INF/
    └── shared_debug.kotlin_module  # Kotlin 模块元数据

3. iOS 编译产物详解

3.1 Framework 结构
shared.framework/
├── Headers/
│   ├── shared.h                # Objective-C 头文件
│   └── shared-Swift.h          # Swift 桥接头文件
├── Modules/
│   ├── module.modulemap        # 模块映射文件
│   └── shared.swiftmodule/     # Swift 模块信息
│       ├── arm64-apple-ios.swiftdoc
│       ├── arm64-apple-ios.swiftinterface
│       └── arm64-apple-ios.swiftmodule
├── Info.plist                  # Framework 信息
├── shared                      # 二进制文件 (机器码)
└── _CodeSignature/             # 代码签名 (Release 版本)
3.2 KLIB 文件详解
shared.klib
├── manifest                    # KLIB 清单文件
├── linkdata/                   # 链接数据
│   ├── module/                 # 模块信息
│   └── package_com.example.kmpdemo/  # 包结构
├── targets/                    # 目标平台信息
│   └── ios_arm64/              # iOS ARM64 特定数据
└── ir/                         # Kotlin IR 中间表示
    ├── files/                  # IR 文件
    └── symbols/                # 符号表

4. XCFramework 详解

XCFramework 是苹果推荐的通用 Framework 格式,支持多架构:

shared.xcframework/
├── Info.plist                  # XCFramework 信息
├── ios-arm64/                  # iOS 真机 (iPhone/iPad)
│   └── shared.framework/
├── ios-arm64_x86_64-simulator/ # iOS 模拟器 (M1 + Intel Mac)
│   └── shared.framework/
└── ios-x86_64-maccatalyst/     # Mac Catalyst (可选)
    └── shared.framework/

优势对比:

格式支持架构Xcode 集成分发便利性
Framework单一架构✅ 原生支持❌ 需要多个文件
Fat Framework多架构合并✅ 原生支持⚠️ 可能被拒审
XCFramework多架构分离✅ 完美支持✅ 单文件分发

6. 编译产物大小对比

产物类型Debug 版本Release 版本说明
Android AAR~500KB~200KB包含 JVM 字节码 + 资源
iOS Framework~2MB~800KB包含原生机器码
KLIB 文件~100KB~50KBKotlin IR 中间表示
XCFramework~6MB~2.5MB包含所有架构的 Framework

7. 编译产物的使用场景

Android 集成
// app/build.gradle.kts
dependencies {
    implementation(project(":shared"))  // 直接依赖 AAR
    // 或者
    implementation("com.example:shared:1.0.0")  // Maven 仓库
}
iOS 集成
// 1. 手动集成 Framework
// Xcode → Target → General → Frameworks, Libraries, and Embedded Content
// 添加 shared.framework

// 2. 使用 XCFramework (推荐)
// 拖拽 shared.xcframework 到 Xcode 项目

// 3. CocoaPods 集成
// Podfile
pod 'shared', :path => '../shared'

8. 编译产物验证

验证 AAR 内容
# 解压 AAR 查看内容
unzip -l shared/build/outputs/aar/shared-debug.aar

# 查看 classes.jar 中的类
jar -tf shared/build/outputs/aar/shared-debug.aar | grep ".class"
验证 Framework 架构
# 查看 Framework 支持的架构
lipo -info shared/build/bin/iosArm64/debugFramework/shared.framework/shared

# 输出: Architectures in the fat file: arm64
验证 XCFramework 信息
# 查看 XCFramework 详细信息
xcodebuild -checkFirstLaunchStatus
plutil -p shared/build/XCFrameworks/debug/shared.xcframework/Info.plist

总结: KMP 编译产物丰富多样,每种产物都有其特定的用途和优势。理解这些产物的结构和用法,有助于更好地进行跨平台开发和集成。

KMP 底层技术原理深度解析

1. Kotlin/Native 编译链

编译流程:

Kotlin 源码 → Kotlin IR → LLVM IR → 平台原生代码

Kotlin IR 详解

Kotlin IR (Intermediate Representation) [发音: /ˈkɒtlɪn aɪ ɑːr/]

  • 定义: Kotlin 编译器的中间表示形式,是一种树状的数据结构
  • 作用: 作为 Kotlin 源码和目标平台代码之间的桥梁
  • 特点:
    • 平台无关的抽象语法树 (AST)
    • 保留了 Kotlin 语言的所有语义信息
    • 支持编译时优化和代码转换
    • 统一了 JVM、Native、JS 等多个后端的编译流程

Kotlin IR 的优势:

// Kotlin 源码
fun greet(name: String): String {
    return "Hello, $name!"
}

// 转换为 Kotlin IR (简化表示)
FUN name:greet visibility:public modality:FINAL <> (name:kotlin.String) returnType:kotlin.String
  BLOCK_BODY
    RETURN type=kotlin.String from='public final fun greet (name: kotlin.String): kotlin.String'
      CALL 'public final fun plus (other: kotlin.Any?): kotlin.String' type=kotlin.String origin=PLUS
        $this: CONST String type=kotlin.String value="Hello, "
        other: GET_VAR 'name:kotlin.String' type=kotlin.String

LLVM IR 详解

LLVM IR (Low Level Virtual Machine Intermediate Representation) [发音: /ˈɛl ɛl viː ɛm aɪ ɑːr/]

  • 定义: LLVM 编译器基础设施的中间表示,是一种低级的、类似汇编的语言
  • 作用: 提供平台无关的低级代码表示,便于优化和目标代码生成
  • 特点:
    • 静态单赋值 (SSA) 形式
    • 强类型系统
    • 无限寄存器架构
    • 支持丰富的优化 passes

LLVM IR 示例:

; 对应上面的 Kotlin greet 函数
define %kotlin.String* @greet(%kotlin.String* %name) {
entry:
  %0 = call %kotlin.String* @"kotlin.String.plus"(%kotlin.String* @"Hello, ", %kotlin.String* %name)
  ret %kotlin.String* %0
}

编译流程对比

阶段Kotlin/JVMKotlin/Native (KMP)
源码解析Kotlin ASTKotlin AST
中间表示Kotlin IR → JVM 字节码Kotlin IR → LLVM IR
目标代码.class 文件原生机器码
运行时JVM原生运行时

为什么需要两层 IR?

  1. Kotlin IR: 保持 Kotlin 语言特性,便于高级优化
  2. LLVM IR: 提供底层抽象,便于目标平台代码生成和底层优化
  • 原生代码: 各平台的机器码 (ARM64, x86_64 等)

2. Commonizer 机制详解

Commonizer 是 KMP 的核心技术,位于 .gradle/kotlin/commonizer/ 目录:

# 普通 Android 项目没有此目录
.gradle/kotlin/commonizer/shared/

Commonizer 的作用:

  1. API 统一化: 将不同平台的相似 API 抽象为通用接口
  2. 依赖解析: 处理跨平台依赖的兼容性
  3. 类型映射: 将平台特定类型映射到通用类型

示例:平台类型统一

// commonMain 中看到的统一 API
expect class Platform {
    val name: String
}

// 实际上 Commonizer 处理了:
// iOS: NSString → String
// Android: java.lang.String → String

3. KLIB 科普:跨平台开发的核心技术

什么是 KLIB?

KLIB (Kotlin Library) [发音: /keɪ lɪb/] 是 Kotlin Multiplatform 生态系统中的一项核心技术,它是 Kotlin/Native 专用的库格式,专门为跨平台原生开发而设计。

简单理解

  • 如果说 JAR 是 Java/Android 的"包装盒"
  • 那么 KLIB 就是 Kotlin Native 平台的"包装盒"
  • 它让 Kotlin 代码能够在 iOS、macOS、Linux、Windows 等原生平台上运行
KLIB 解决了什么问题?

传统跨平台开发的痛点

┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   Android   │    │     iOS     │    │   Desktop   │
│             │    │             │    │             │
│ Java/Kotlin │    │ Swift/ObjC  │    │   C++/C#    │
│   字节码     │    │   机器码     │    │   机器码     │
└─────────────┘    └─────────────┘    └─────────────┘
     ↑                   ↑                   ↑
  不同语言            不同语言            不同语言
  不同运行时          不同运行时          不同运行时

KLIB 的解决方案

┌─────────────────────────────────────────────────────┐
│                 Kotlin 源代码                        │
│            (commonMain + 平台特定代码)                │
└─────────────────────────────────────────────────────┘
                         │
                    Kotlin 编译器
                         │
                         ▼
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   Android   │    │     iOS     │    │   Desktop   │
│             │    │             │    │             │
│   AAR/JAR   │    │    KLIB     │    │    KLIB     │
│   (字节码)   │    │  (原生码)    │    │  (原生码)    │
└─────────────┘    └─────────────┘    └─────────────┘
KLIB 支持哪些平台?

KLIB 不仅仅是为 iOS 设计的! 它支持所有 Kotlin/Native 目标平台:

平台类别支持的目标说明
移动端iosArm64, iosX64, iosSimulatorArm64iPhone、iPad、iOS 模拟器
桌面端macosX64, macosArm64, linuxX64, mingwX64Mac、Linux、Windows
嵌入式linuxArm64, androidNativeX64ARM 设备、Android NDK
其他watchosArm64, tvosArm64Apple Watch、Apple TV
为什么 KLIB 很重要?

1. 真正的代码复用

// 这段代码写一次,编译到所有 Native 平台
class UserRepository {
    fun getUser(id: Int): User? {
        // 业务逻辑在所有平台完全一致
        return database.findUser(id)
    }
}

2. 类型安全保证

// KLIB 确保跨平台类型一致性
expect class Platform {
    val name: String  // 编译时检查所有平台实现
}

// iOS 实现
actual class Platform {
    actual val name: String = "iOS"  // ✅ 类型匹配
}

// 如果类型不匹配,编译直接报错,避免运行时问题

3. 性能优化

传统方案:Kotlin → JVM 字节码 → 解释执行 (慢)
KLIB 方案:Kotlin → KLIB → 原生机器码 (快)

4. KLIB (Kotlin Library) 格式详解

KLIB (Kotlin Library) 是 Kotlin/Native 和 Kotlin Multiplatform 的专用库格式,包含编译后的 Kotlin 代码和元数据。

KLIB 的自动生成机制

无需额外依赖! KLIB 文件是通过 Kotlin Multiplatform 插件自动生成的:

// shared/build.gradle.kts
plugins {
    kotlin("multiplatform")  // 这个插件负责生成 KLIB
    id("com.android.library")
}

kotlin {
    // 每个目标平台都会生成对应的 KLIB
    listOf(
        iosX64(),           // → iosX64/main/klib/
        iosArm64(),         // → iosArm64/main/klib/  
        iosSimulatorArm64() // → iosSimulatorArm64/main/klib/
    ).forEach {
        it.binaries.framework {
            baseName = "shared"
        }
    }
}
KLIB 文件结构分析

shared/build/classes/kotlin/ 目录下,每个平台都有对应的 KLIB:

shared/build/classes/kotlin/
├── iosArm64/main/klib/          # iOS ARM64 KLIB 文件
│   └── shared.klib              # 包含 ARM64 优化的机器码
├── iosX64/main/klib/            # iOS x64 KLIB 文件  
│   └── shared.klib              # 包含 x86_64 优化的机器码
├── iosSimulatorArm64/main/klib/ # iOS 模拟器 ARM64 KLIB
│   └── shared.klib              # 包含模拟器专用代码
└── metadata/                    # 平台无关元数据
    ├── commonMain/              # 通用代码元数据
    └── iosMain/                 # iOS 特定元数据
KLIB vs JAR vs AAR 对比
格式平台内容运行时链接方式
JARJVMJava 字节码JVM动态加载
AARAndroidAndroid 资源 + JARART/DalvikAPK 打包
KLIBNativeKotlin IR + 元数据原生静态链接
KLIB 的技术优势

1. 编译时优化

// commonMain 源码
fun calculateSum(numbers: List<Int>): Int {
    return numbers.sum()
}

// KLIB 中保存的不是字节码,而是优化后的 Kotlin IR
// 在最终编译时会针对目标平台进行深度优化

2. 跨平台元数据

// KLIB 包含完整的类型信息
expect class Platform {
    val name: String  // 元数据保证类型安全
}

// 编译时验证所有平台实现的一致性
actual class Platform {
    actual val name: String = "iOS"  // ✅ 类型匹配
}

3. 增量编译支持

# 只有修改的模块会重新生成 KLIB
$ ./gradlew :shared:compileKotlinIosArm64
> Task :shared:compileKotlinIosArm64 UP-TO-DATE  # 增量编译
KLIB 生成时机
# 执行以下任务时自动生成 KLIB
./gradlew :shared:compileKotlinIosArm64        # 生成 iOS ARM64 KLIB
./gradlew :shared:compileKotlinIosX64          # 生成 iOS x64 KLIB
./gradlew :shared:linkDebugFrameworkIosArm64   # 链接生成 Framework

总结: KLIB 是 KMP 项目的核心产物,无需额外配置即可自动生成,它保证了跨平台代码的类型安全和性能优化。

5. iOS Framework 生成机制

关键产物分析:

# iOS Framework 结构
shared.framework/
├── Headers/shared.h        # Objective-C 头文件
├── Modules/module.modulemap # 模块映射
├── Info.plist             # Framework 信息
└── shared                 # 二进制文件 (ARM64/x86_64)

重要说明:Framework 产物解析

常见误解: KMP 不会生成源码映射,而是直接编译为二进制文件!

实际情况:

  • shared.framework/shared 是编译后的二进制文件
  • shared.framework/Headers/shared.h 包含 Objective-C 接口声明
  • iOS 工程通过头文件调用二进制中的函数

头文件示例 (shared.h):

// 自动生成的 Objective-C 接口
@interface SharedGreeting : SharedBase
- (NSString *)greet __attribute__((swift_name("greet()")));
@end

@interface SharedPlatform : SharedBase
- (NSString *)name __attribute__((swift_name("name")));
@end

二进制调用流程:

iOS App → shared.h (接口) → shared (二进制) → Kotlin 逻辑

7. 多架构支持机制

Fat Framework 技术:

shared/build/fat-framework/debug/shared.framework

包含多个架构的二进制文件:

  • iosArm64: iPhone/iPad 真机 (ARM64)
  • iosSimulatorArm64: M1 Mac 模拟器 (ARM64)
  • iosX64: Intel Mac 模拟器 (x86_64)

lipo 工具合并:

# KMP 自动执行类似操作
lipo -create \
  iosArm64/shared \
  iosSimulatorArm64/shared \
  iosX64/shared \
  -output shared.framework/shared

iOS 工程使用 KMP Framework 详解

1. Framework 集成步骤

步骤 1: 配置 XCFramework 支持

⚠️ 重要: 默认的 KMP 项目模板不包含 XCFramework 配置,需要手动添加。

shared/build.gradle.kts 中添加以下配置:

// 1. 添加导入
import org.jetbrains.kotlin.gradle.plugin.mpp.apple.XCFramework

// 2. 在 kotlin 块中配置 XCFramework
kotlin {
    // XCFramework 配置
    val xcf = XCFramework()
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = "shared"
            xcf.add(this)  // 🔥 关键:将 framework 添加到 XCFramework
            isStatic = true  // 推荐使用静态库
        }
    }
}

步骤 2: 构建 XCFramework

# 在 KMP 项目根目录执行
./gradlew :shared:assembleXCFramework

# 构建成功后,XCFramework 将生成在:
# shared/build/XCFrameworks/debug/shared.xcframework
# shared/build/XCFrameworks/release/shared.xcframework

步骤 3: 导入到 iOS 项目

1. 打开 Xcode 项目
2. 选择 Target → General → Frameworks, Libraries, and Embedded Content
3. 点击 "+" → Add Other → Add Files
4. 选择 shared/build/XCFrameworks/debug/shared.xcframework
5. 设置 Embed & Sign

步骤 3: 配置 Build Settings

Framework Search Paths: $(PROJECT_DIR)/../shared/build/XCFrameworks/debug
Other Linker Flags: -framework shared

2. Swift 代码调用示例

导入 Framework:

import shared

调用共享代码:

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 调用 KMP 共享逻辑
        let greeting = Greeting().greet()
        let platform = Platform().name
        
        print("Greeting: \(greeting)")
        print("Platform: \(platform)")
        
        // 使用共享的业务逻辑
        let calculator = Calculator()
        let result = calculator.add(a: 5, b: 3)
        print("5 + 3 = \(result)")
    }
}

3. Objective-C 代码调用示例

导入头文件:

#import <shared/shared.h>

调用共享代码:

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 调用 KMP 共享逻辑
    SharedGreeting *greeting = [[SharedGreeting alloc] init];
    NSString *greetingText = [greeting greet];
    
    SharedPlatform *platform = [[SharedPlatform alloc] init];
    NSString *platformName = [platform name];
    
    NSLog(@"Greeting: %@", greetingText);
    NSLog(@"Platform: %@", platformName);
    
    // 使用共享的业务逻辑
    SharedCalculator *calculator = [[SharedCalculator alloc] init];
    int32_t result = [calculator addA:5 b:3];
    NSLog(@"5 + 3 = %d", result);
}

@end

4. 自动化集成 (推荐)

使用 CocoaPods:

# Podfile
platform :ios, '11.0'
use_frameworks!

target 'iosApp' do
    pod 'shared', :path => '../shared'
end

Gradle 自动复制:

// shared/build.gradle.kts
kotlin {
    cocoapods {
        summary = "Some description for the Shared Module"
        homepage = "Link to the Shared Module homepage"
        version = "1.0"
        ios.deploymentTarget = "14.1"
        podfile = project.file("../iosApp/Podfile")
        
        framework {
            baseName = "shared"
            isStatic = true
        }
    }
}

5. 调试和性能监控

Xcode 调试支持:

  • ✅ 可以在 Swift/ObjC 代码中设置断点
  • ❌ 无法直接调试 Kotlin 代码 (需要 AppCode)
  • ✅ 可以查看 Kotlin 对象的属性值

性能分析:

// 使用 Instruments 分析 KMP 调用性能
let startTime = CFAbsoluteTimeGetCurrent()
let result = SharedBusinessLogic().processData(data)
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
print("KMP call took \(timeElapsed) seconds")

技术栈对比

组件普通 AndroidKMP 项目
编译器kotlinc-jvmkotlinc-jvm + kotlinc-native
运行时ART (Android Runtime)ART + Native Runtime
构建产物APK/AABAAR + Framework
依赖管理Gradle (JVM)Gradle + CocoaPods
调试Android StudioAndroid Studio + Xcode
内存管理GC (垃圾回收)GC + ARC (自动引用计数)

版本信息

  • Kotlin: 1.9.10
  • Android Gradle Plugin: 7.4.2
  • Gradle: 8.2.1
  • Android SDK:
    • compileSdk: 34
    • minSdk: 24
    • targetSdk: 34
  • JVM Target: 1.8

KMP 编译过程深度分析

1. 编译时序图

Kotlin CompilerCommonizerKotlin/NativeLLVMXcode Tools解析 commonMain 源码生成平台无关 IR处理平台差异统一化编译为 JVM 字节码生成 AARKotlin IR → LLVM IRLLVM IR → ARM64/x86_64 机器码链接生成 Frameworkpar[Android 编译分支][iOS 编译分支]Kotlin CompilerCommonizerKotlin/NativeLLVMXcode Tools

2. 实际编译日志分析

普通 Android 项目编译:

> Task :app:compileDebugKotlin
> Task :app:compileDebugJavaWithJavac  
> Task :app:mergeDebugResources
> Task :app:packageDebug
# 总共约 10-15 个任务

KMP 项目编译:

> Task :shared:compileCommonMainKotlinMetadata
> Task :shared:compileDebugKotlinAndroid
> Task :shared:compileKotlinIosArm64
> Task :shared:compileKotlinIosSimulatorArm64  
> Task :shared:compileKotlinIosX64
> Task :shared:linkDebugFrameworkIosArm64
> Task :shared:linkDebugFrameworkIosSimulatorArm64
> Task :shared:linkDebugFrameworkIosX64
> Task :shared:assembleFatFrameworkDebug
# 总共约 50+ 个任务,包含多平台编译

3. 内存占用对比

项目类型编译内存峰值构建产物大小编译时间
普通 Android~2GBAPK: 5-10MB30-60s
KMP 项目~4GBAAR + Framework: 15-25MB90-180s

工程创建过程

1. 项目初始化

1.1 创建项目结构
mkdir KMPDemo
cd KMPDemo
1.2 配置 Gradle 设置

创建 settings.gradle.kts

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

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

rootProject.name = "KMPDemo"
include(":androidApp")
include(":shared")
1.3 版本管理配置

创建 gradle/libs.versions.toml

[versions]
agp = "7.4.2"
kotlin = "1.9.10"
android-compileSdk = "34"
android-minSdk = "24"
android-targetSdk = "34"

[libraries]
kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
androidx-core-ktx = "androidx.core:core-ktx:1.9.0"
androidx-lifecycle-runtime-ktx = "androidx.lifecycle:lifecycle-runtime-ktx:2.6.1"
androidx-appcompat = "androidx.appcompat:appcompat:1.6.1"
material = "com.google.android.material:material:1.8.0"

[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
androidLibrary = { id = "com.android.library", version.ref = "agp" }
kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }

2. 共享模块配置

2.1 构建脚本 (shared/build.gradle.kts)
plugins {
    kotlin("multiplatform")
    id("com.android.library")
}

kotlin {
    android {
        compilations.all {
            kotlinOptions {
                jvmTarget = "1.8"
            }
        }
    }
    
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = "shared"
        }
    }

    sourceSets {
        val commonMain by getting
        val commonTest by getting {
            dependencies {
                implementation(kotlin("test"))
            }
        }
        val androidMain by getting
        val androidUnitTest by getting
        val iosX64Main by getting
        val iosArm64Main by getting
        val iosSimulatorArm64Main by getting
        val iosMain by creating {
            dependsOn(commonMain)
            iosX64Main.dependsOn(this)
            iosArm64Main.dependsOn(this)
            iosSimulatorArm64Main.dependsOn(this)
        }
        val iosX64Test by getting
        val iosArm64Test by getting
        val iosSimulatorArm64Test by getting
        val iosTest by creating {
            dependsOn(commonTest)
            iosX64Test.dependsOn(this)
            iosArm64Test.dependsOn(this)
            iosSimulatorArm64Test.dependsOn(this)
        }
    }
}

android {
    namespace = "com.example.kmpdemo.shared"
    compileSdk = 34
    sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
    defaultConfig {
        minSdk = 24
        targetSdk = 34
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
}
2.2 共享业务逻辑

平台信息 (shared/src/commonMain/kotlin/Platform.kt)

interface Platform {
    val name: String
}

expect fun getPlatform(): Platform

Android 实现 (shared/src/androidMain/kotlin/Platform.android.kt)

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

actual fun getPlatform(): Platform = AndroidPlatform()

iOS 实现 (shared/src/iosMain/kotlin/Platform.ios.kt)

import platform.UIKit.UIDevice

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

actual fun getPlatform(): Platform = IOSPlatform()

业务逻辑 (shared/src/commonMain/kotlin/Greeting.kt)

class Greeting {
    private val platform = getPlatform()

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

data class User(
    val id: Int,
    val name: String,
    val email: String
)

class UserRepository {
    private val users = mutableListOf<User>()
    
    init {
        // 添加一些示例数据
        users.addAll(listOf(
            User(1, "张三", "zhangsan@example.com"),
            User(2, "李四", "lisi@example.com"),
            User(3, "王五", "wangwu@example.com")
        ))
    }
    
    fun getAllUsers(): List<User> = users.toList()
    
    fun addUser(user: User) {
        users.add(user)
    }
    
    fun getUserById(id: Int): User? = users.find { it.id == id }
    
    fun deleteUser(id: Int): Boolean {
        val userToRemove = users.find { it.id == id }
        return if (userToRemove != null) {
            users.remove(userToRemove)
        } else {
            false
        }
    }
    
    fun getUserCount(): Int = users.size
}

3. Android 应用模块

3.1 构建脚本 (androidApp/build.gradle.kts)
plugins {
    id("com.android.application")
    kotlin("android")
}

android {
    namespace = "com.example.kmpdemo.androidApp"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.example.kmpdemo.androidApp"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"
    }
    packagingOptions {
        resources {
            excludes += listOf("/META-INF/{AL2.0,LGPL2.1}")
        }
    }
    buildTypes {
        getByName("release") {
            isMinifyEnabled = false
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

dependencies {
    implementation(project(":shared"))
    implementation("androidx.core:core-ktx:1.9.0")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
    implementation("androidx.appcompat:appcompat:1.6.1")
    implementation("com.google.android.material:material:1.8.0")
}
3.2 主活动实现
package com.example.kmpdemo.androidApp

import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import Greeting
import UserRepository

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val greeting = Greeting()
        val userRepository = UserRepository()
        
        val textView = TextView(this).apply {
            text = buildString {
                appendLine(greeting.greet())
                appendLine("\n用户数量: ${userRepository.getUserCount()}")
                appendLine("\n用户列表:")
                userRepository.getAllUsers().forEach { user ->
                    appendLine("- ${user.name} (${user.email})")
                }
            }
            textSize = 16f
            setPadding(32, 32, 32, 32)
        }
        
        setContentView(textView)
    }
}

核心代码实现分析

1. expect/actual 机制深度解析

commonMain 中的期望声明:

// Platform.kt - 平台抽象
interface Platform {
    val name: String
}

expect fun getPlatform(): Platform

平台特定实现:

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

actual fun getPlatform(): Platform = AndroidPlatform()

// Platform.ios.kt - iOS 实现  
class IOSPlatform : Platform {
    override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
}

actual fun getPlatform(): Platform = IOSPlatform()

编译时解析:

  • Android 编译: 只看到 AndroidPlatform 实现
  • iOS 编译: 只看到 IOSPlatform 实现
  • IDE 智能提示: 同时显示两个平台的 API

2. 跨平台业务逻辑实现

// 完全跨平台的业务逻辑
class UserRepository {
    private val users = mutableListOf<User>()
    
    fun addUser(user: User) {
        users.add(user)
        // 这段代码在 Android 和 iOS 上完全一致
        println("User added on ${getPlatform().name}")
    }
    
    fun getUserById(id: Int): User? {
        // 集合操作跨平台兼容
        return users.find { it.id == id }
    }
    
    fun getAllUsers(): List<User> {
        // 返回不可变列表,跨平台安全
        return users.toList()
    }
}

3. 内存管理差异分析

Android (JVM) 内存模型:

val repository = UserRepository() // 堆内存分配
val user = User(1, "Alice", "alice@example.com") // GC 管理
repository.addUser(user) // 引用传递
// GC 自动回收不再使用的对象

iOS (Native) 内存模型:

// 相同的 Kotlin 代码
val repository = UserRepository() // 原生内存分配
val user = User(1, "Alice", "alice@example.com") // ARC 管理
repository.addUser(user) // 值拷贝/引用计数
// ARC 自动管理引用计数

遇到的问题及解决方案

1. JVM 目标版本不兼容 🔥

问题:

JVM target version mismatch: 
compileDebugJavaWithJavac (1.8) vs compileDebugKotlinAndroid (11)

根本原因: KMP 默认使用较新的 JVM 目标版本,与 Android 项目不匹配。

解决方案:

// shared/build.gradle.kts
kotlin {
    android {
        compilations.all {
            kotlinOptions {
                jvmTarget = "1.8" // 🔥 关键配置
            }
        }
    }
}

2. 多平台 API 兼容性 🔥

问题: removeIf 等 API 在不同平台表现不一致。

解决方案: 使用 KMP 兼容的实现模式:

// ❌ 不兼容写法
users.removeIf { it.id == id }

// ✅ KMP 兼容写法
val userToRemove = users.find { it.id == id }
if (userToRemove != null) {
    users.remove(userToRemove)
    return true
}
return false

3. Framework 链接问题 🔥

问题: iOS Framework 生成失败,缺少必要的链接库。

解决方案: 确保 Xcode Command Line Tools 已安装:

xcode-select --install

4. Gradle Wrapper 缺失

问题: 执行 ./gradlew build 时提示文件不存在
解决方案:

  1. 手动创建 gradlew 脚本
  2. 下载 gradle-wrapper.jar
  3. 配置 gradle-wrapper.properties

5. Android 命名空间缺失

问题: 构建时提示需要指定 namespace
解决方案: 在 android 块中添加 namespace 配置

如何使用

1. 环境要求

  • JDK 11 或更高版本
  • Android Studio Arctic Fox 或更高版本
  • Xcode 13+ (iOS 开发)

2. 项目构建

构建整个项目
./gradlew build
构建 Android APK
./gradlew :androidApp:assembleDebug
清理项目
./gradlew clean

3. 运行应用

Android
  1. 在 Android Studio 中打开项目
  2. 选择 androidApp 配置
  3. 点击运行按钮或使用快捷键 Ctrl+R
iOS (需要 macOS)
  1. 在 Xcode 中打开 iosApp 项目
  2. 选择目标设备或模拟器
  3. 点击运行按钮

4. 开发指南

添加新的共享功能
  1. shared/src/commonMain/kotlin/ 中创建新的 Kotlin 文件
  2. 如需平台特定实现,使用 expect/actual 机制
  3. 在各平台模块中调用共享代码
添加依赖
  1. gradle/libs.versions.toml 中定义版本
  2. 在相应模块的 build.gradle.kts 中添加依赖
平台特定代码
  • Android: shared/src/androidMain/kotlin/
  • iOS: shared/src/iosMain/kotlin/
  • 通用: shared/src/commonMain/kotlin/

KMP 实际应用价值分析

1. 开发效率提升

传统开发模式:

业务需求 → Android 开发 (2周) + iOS 开发 (2周) = 4周
维护成本:双倍代码量,双倍 Bug 修复时间

KMP 开发模式:

业务需求 → 共享逻辑开发 (1.5周) + 平台适配 (1周) = 2.5周
维护成本:70% 代码复用,Bug 修复一次生效两个平台

2. 团队协作优化

技能要求变化:

  • 传统: Android 工程师 + iOS 工程师 (2个专业领域)
  • KMP: Kotlin 工程师 + 平台适配工程师 (1个核心技能)

沟通成本降低:

  • 业务逻辑统一实现,减少平台间的理解偏差
  • API 接口自动保持一致,避免版本不同步问题

3. 质量保证提升

类型安全:

// 编译时保证跨平台类型一致性
expect class NetworkClient {
    suspend fun fetchUser(id: Int): User // 返回类型强制一致
}

测试覆盖:

// 一套测试用例覆盖所有平台
class UserRepositoryTest {
    @Test
    fun testAddUser() {
        // 这个测试在 Android 和 iOS 上都会执行
        val repo = UserRepository()
        val user = User(1, "Test", "test@example.com")
        repo.addUser(user)
        assertEquals(1, repo.getUserCount())
    }
}

企业级应用建议

1. 渐进式迁移策略

阶段 1: 工具类迁移

// 先迁移无状态的工具类
object DateUtils {
    fun formatDate(timestamp: Long): String { ... }
    fun parseDate(dateString: String): Long { ... }
}

阶段 2: 数据模型统一

// 统一数据模型定义
@Serializable
data class User(val id: Int, val name: String, val email: String)

阶段 3: 业务逻辑迁移

// 核心业务逻辑跨平台化
class UserService {
    suspend fun login(username: String, password: String): LoginResult
    suspend fun fetchProfile(): UserProfile
}

2. 架构设计最佳实践

分层架构:

┌─────────────────────────────────────┐
│           Platform Layer            │ ← Android Activity/iOS ViewController
├─────────────────────────────────────┤
│         Presentation Layer          │ ← ViewModels/Presenters (共享)
├─────────────────────────────────────┤  
│          Business Layer             │ ← Use Cases/Interactors (共享)
├─────────────────────────────────────┤
│            Data Layer               │ ← Repositories (共享)
├─────────────────────────────────────┤
│          Network/Storage            │ ← Platform-specific implementations
└─────────────────────────────────────┘

依赖注入配置:

// commonMain
expect fun platformModule(): Module

// androidMain  
actual fun platformModule() = module {
    single<NetworkClient> { AndroidNetworkClient() }
}

// iosMain
actual fun platformModule() = module {
    single<NetworkClient> { IOSNetworkClient() }
}

总结

这个 KMP 项目展示了 Kotlin Multiplatform 的核心优势:

🎯 技术价值:

  • 70% 代码复用率,显著提升开发效率
  • 编译时类型安全,减少运行时错误

🏗️ 架构价值:

  • 统一业务逻辑,降低维护成本
  • 平台特定优化,保持原生体验
  • 渐进式迁移,降低技术风险

👥 团队价值:

  • 技能栈统一,降低人员成本
  • 沟通效率提升,减少理解偏差
  • 质量保证增强,一次开发多端受益

KMP 不仅仅是一个跨平台解决方案,更是现代移动开发的技术演进方向。通过深入理解其底层原理和编译机制,我们能够更好地利用这项技术为企业创造价值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

低调小一

您的鼓励是我创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值