Egloo 中Kotlin 多平台中的 expect/actual

Kotlin 多平台中的 expect/actual 机制详解

1. 基本概念

1.1 什么是 expect/actual 机制

expect/actual 是 Kotlin 多平台项目中用于实现平台特定代码的核心机制。它允许我们在共享代码中声明 API 契约(使用 expect 关键字),然后在各个平台特定代码中提供实现(使用 actual 关键字)。

1.2 基本语法

kotlin 复制代码
// 在共享代码中声明
expect fun platformName(): String

// 在各平台代码中实现
actual fun platformName(): String = "Android" // Android 实现
actual fun platformName(): String = "iOS"     // iOS 实现
actual fun platformName(): String = "Web"     // Web 实现

2. 多种声明类型

2.1 函数声明

kotlin 复制代码
// 共享代码
expect fun readFile(path: String): String

// 平台实现
actual fun readFile(path: String): String {
    // 平台特定的文件读取实现
}

2.2 属性声明

kotlin 复制代码
// 共享代码
expect val platformVersion: String

// 平台实现
actual val platformVersion: String = "Android 12"

2.3 类声明

kotlin 复制代码
// 共享代码
expect class PlatformFile(path: String) {
    fun read(): ByteArray
    fun write(data: ByteArray)
}

// 平台实现
actual class PlatformFile actual constructor(private val path: String) {
    actual fun read(): ByteArray {
        // 平台特定实现
    }
    
    actual fun write(data: ByteArray) {
        // 平台特定实现
    }
}

2.4 对象声明

kotlin 复制代码
// 共享代码
expect object PlatformInfo {
    val name: String
    fun getVersion(): String
}

// 平台实现
actual object PlatformInfo {
    actual val name: String = "Android"
    actual fun getVersion(): String = "12.0"
}

2.5 继承模式

kotlin 复制代码
// 共享代码中的基类
open class BaseLogger {
    open fun log(message: String) {
        // 共享的日志逻辑
    }
}

// 共享代码中的 expect 声明
expect class PlatformLogger : BaseLogger

// 平台实现
actual class PlatformLogger : BaseLogger() {
    override fun log(message: String) {
        super.log(message)
        // 平台特定的额外日志逻辑
    }
}

3. 实际项目中的应用案例(基于 Egloo 项目)

3.1 平台特定类型和常量

kotlin 复制代码
// 在 commonMain 中声明平台特定的类型
internal expect class EglSurface
internal expect class EglContext
internal expect class EglDisplay
internal expect class EglConfig

// 在 commonMain 中声明平台特定的常量
internal expect val EGL_NO_CONTEXT: EglContext
internal expect val EGL_NO_DISPLAY: EglDisplay
internal expect val EGL_SUCCESS: Int

3.2 平台特定 API 函数

kotlin 复制代码
// 在 commonMain 中声明平台特定的函数
internal expect inline fun eglChooseConfig(
    display: EglDisplay, 
    attributes: IntArray, 
    configs: Array<EglConfig?>, 
    configsSize: Int, 
    numConfigs: IntArray
): Boolean

internal expect inline fun eglInitialize(
    display: EglDisplay, 
    major: IntArray, 
    minor: IntArray
): Boolean

3.3 工厂模式实现

kotlin 复制代码
// 在 commonMain 中定义工厂接口
interface GlProgramFactory {
    fun createProgram(): GlProgram
}

// 使用 expect/actual 声明工厂提供者
internal expect object GlProgramFactorySingleton {
    fun getInstance(): GlProgramFactory
}

// 在平台特定代码中实现
internal actual object GlProgramFactorySingleton {
    fun getInstance(): GlProgramFactory {
        return GlProgramFactoryImpl()
    }
}

3.4 继承与共享实现

kotlin 复制代码
// EglNativeCore.kt - 共享基类
public open class EglNativeCore internal constructor(
    sharedContext: EglContext = EGL_NO_CONTEXT, 
    flags: Int = 0
) {
    // 共享的实现代码
    internal open fun release() {
        // 共享的清理逻辑
    }
}

// 在 commonMain 中声明继承自基类的 expect 类
public expect class EglCore : EglNativeCore

// 在平台特定代码中实现
public actual class EglCore : EglNativeCore {
    // 平台特定实现
}

4. 常见陷阱和注意事项

4.1 修饰符使用错误

4.1.1 对象方法的修饰符
kotlin 复制代码
// 在 commonMain 中
expect object MySingleton {
    fun getInstance(): MyClass
}

// ❌ 错误方式 1:不要在方法上使用 actual
actual object MySingleton {
    actual fun getInstance(): MyClass  // 错误:不需要 actual
}

// ❌ 错误方式 2:不要添加 override
actual object MySingleton {
    override fun getInstance(): MyClass  // 错误:不需要 override
}

// ✅ 正确方式:不使用任何额外修饰符
actual object MySingleton {
    fun getInstance(): MyClass {
        return MyClassImpl()
    }
}
4.1.2 类成员的修饰符
kotlin 复制代码
// 在 commonMain 中
expect class PlatformFile(path: String) {
    fun read(): ByteArray
}

// 在平台实现中
actual class PlatformFile actual constructor(private val path: String) {
    // 这里需要 actual 修饰符,因为是类成员函数
    actual fun read(): ByteArray {
        // 实现
    }
}

4.2 可见性不匹配

kotlin 复制代码
// 在 commonMain 中
internal expect object Factory

// ✅ 正确:可见性匹配
internal actual object Factory  

// ✅ 正确:可见性更开放
public actual object Factory    

// ❌ 错误:可见性更严格
private actual object Factory   

4.3 缺少平台实现

如果某个平台没有提供 actual 实现,编译器会报错:

arduino 复制代码
Expected declaration has no actual declaration in module [platform-module] for [target-platform]

4.4 类型签名不匹配

kotlin 复制代码
// 在 commonMain 中
expect fun process(data: String): Int

// ❌ 错误:返回类型不匹配
actual fun process(data: String): Long = 42L

// ❌ 错误:参数类型不匹配
actual fun process(data: Int): Int = data

5. 最佳实践

5.1 模块化设计

kotlin 复制代码
// 分离平台特定的常量
internal expect val EGL_SUCCESS: Int
internal expect val EGL_NONE: Int

// 分离平台特定的类型
internal expect class EglSurface
internal expect class EglContext

// 分离平台特定的操作
internal expect inline fun eglInitialize(...): Boolean

5.2 共享代码最大化

kotlin 复制代码
// 基类包含共享实现
open class BaseProcessor {
    // 共享的处理逻辑
    fun process(data: String): String {
        val preprocessed = preprocess(data)
        return platformSpecificProcess(preprocessed)
    }
    
    private fun preprocess(data: String): String {
        // 共享的预处理逻辑
        return data.trim()
    }
    
    // 平台特定部分声明为 expect
    protected expect fun platformSpecificProcess(data: String): String
}

// 平台实现只需关注特定部分
actual class AndroidProcessor : BaseProcessor() {
    actual override fun platformSpecificProcess(data: String): String {
        // Android 特定实现
    }
}

5.3 可见性控制

kotlin 复制代码
// 公共 API 保持最小化
public expect class PlatformLogger

// 内部实现使用 internal
internal expect fun logToNativePlatform(message: String)

5.4 使用检查清单

  • expect 声明放在 commonMain 源集中
  • 所有目标平台都提供了 actual 实现
  • 没有在对象方法上使用多余的 actual/override 修饰符
  • 可见性修饰符正确匹配
  • 类型签名在所有平台上保持一致

6. 性能考虑

6.1 内联函数

kotlin 复制代码
// 使用 inline 提高性能,避免函数调用开销
internal expect inline fun computeHash(data: ByteArray): Int

6.2 编译时解析

expect/actual 机制是在编译时解析的,没有运行时开销。编译器会为每个平台生成包含正确实现的代码。

6.3 单例模式优化

kotlin 复制代码
// 在共享代码中声明单例
expect object PlatformSingleton {
    fun getInstance(): Platform
}

// 在平台实现中高效实现
actual object PlatformSingleton {
    // 平台特定的高效单例实现
    private val instance by lazy { PlatformImpl() }
    
    fun getInstance(): Platform = instance
}

7. 测试策略

7.1 共享测试

kotlin 复制代码
// commonTest
class CommonTests {
    @Test
    fun testFactoryInterface() {
        val factory = GlProgramFactorySingleton.getInstance()
        assertNotNull(factory)
        // 测试共享接口行为
    }
}

7.2 平台特定测试

kotlin 复制代码
// androidTest
class AndroidTests {
    @Test
    fun testFactoryImplementation() {
        val factory = GlProgramFactorySingleton.getInstance()
        assertTrue(factory is GlProgramFactoryImpl)
        // 测试平台特定实现细节
    }
}

7.3 模拟平台依赖

kotlin 复制代码
// 在测试中替换平台实现
class MockPlatformFile : PlatformFile {
    override fun read(): ByteArray = "test data".toByteArray()
    override fun write(data: ByteArray) { /* 不执行实际写入 */ }
}

8. 项目结构设计

8.1 典型的源集结构

bash 复制代码
src/
├── commonMain/            # 共享代码
├── androidMain/          # Android 平台通用代码
├── androidJvmMain/      # Android JVM 特定代码
├── androidNativeMain/   # Android Native 通用代码
├── iosMain/             # iOS 平台代码
├── jsMain/              # JavaScript 平台代码
└── jvmMain/             # JVM 平台代码

8.2 依赖管理

kotlin 复制代码
// build.gradle.kts
kotlin {
    sourceSets {
        val commonMain by getting {
            dependencies {
                // 共享依赖
            }
        }
        val androidMain by getting {
            dependencies {
                // Android 特定依赖
            }
        }
        val iosMain by getting {
            dependencies {
                // iOS 特定依赖
            }
        }
    }
}

总结

Kotlin 多平台中的 expect/actual 机制是实现跨平台代码共享的核心技术。通过在共享代码中定义接口契约,并在各平台提供具体实现,我们可以最大化代码复用,同时保持平台特定功能的灵活性。

关键要点:

  1. 使用 expect 在共享代码中声明 API 契约
  2. 使用 actual 在平台特定代码中提供实现
  3. 避免在 actual 对象的方法上使用不必要的修饰符
  4. 保持一致的可见性和类型签名
  5. 最大化共享代码,只将平台特定部分声明为 expect
  6. 遵循单一职责原则,每个 expect 声明都应该有明确的用途

通过正确使用 expect/actual 机制,我们可以构建真正跨平台的 Kotlin 应用,在不同平台上提供一致的功能和最佳的性能。

相关推荐
雨白1 小时前
开发 SunnyWeather:Android 天气预报 App(下)
android
_extraordinary_2 小时前
Java 字符串常量池 +反射,枚举和lambda表达式
android·java·开发语言
alexhilton2 小时前
学会说不!让你彻底学会Kotlin Flow的取消机制
android·kotlin·android jetpack
来来走走3 小时前
Flutter dart运算符
android·前端·flutter
青小莫3 小时前
IDM下载失败常见原因
android
阿华的代码王国3 小时前
【Android】日期选择器
android·xml·java·前端·后端
小墙程序员5 小时前
Android 性能优化(五)Heap Dump 的使用
android·性能优化
阿华的代码王国5 小时前
【Android】RecyclerView实现新闻列表布局(1)适配器使用相关问题
android·xml·java·前端·后端
EngZegNgi5 小时前
Unity —— Android 应用构建与发布
android·unity·自动化·游戏引擎·构建
fatiaozhang95275 小时前
烽火HG680-KX-海思MV320芯片-2+8G-安卓9.0-强刷卡刷固件包
android·电视盒子·刷机固件·机顶盒刷机