Skip to content

原生互操作深度实战

源:Kotlin/Native C 互操作官方文档

本章将带您进入 Kotlin/Native 的深水区,探讨如何以工业级的标准处理 C 库集成、内存管理和异步回调。

复杂结构体映射

C 库中充满了嵌套结构体。在 Kotlin 中,这些结构体被映射为继承自 CStructVar 的类。

实战:操作复杂的 Win32 或 POSIX 结构体

kotlin
import kotlinx.cinterop.*
import platform.posix.*

fun processStat() {
    memScoped {
        val st = alloc<stat>() // 在当前作用域分配结构体内存
        if (stat("file.txt", st.ptr) == 0) {
            val size = st.st_size
            val mtime = st.st_mtime
            println("File size: $size, Modified at: $mtime")
        }
    } // 离开作用域后自动释放
}

零拷贝技术:直接操作 C 数组

在处理音频、图像或网络原始包时,避免内存拷贝是性能优化的核心。

kotlin
// 假设你从 C 库得到了一个指针和长度
fun processNativeData(dataPtr: CPointer<ByteVar>, length: Int) {
    // ❌ 错误做法:转换为 Kotlin ByteArray (会发生全量拷贝)
    // val ktArray = dataPtr.readBytes(length) 

    // ✅ 正确做法:直接在 Kotlin 中通过指针遍历
    for (i in 0 until length) {
        val byte = dataPtr[i]
        // 直接处理字节逻辑
    }
}

深度技术:C 异步回调与 Kotlin 协程

这是最难的部分。C 函数通常接收一个函数指针作为回调。

模式:利用 StableRef 桥接

由于 staticCFunction 无法捕获闭包,我们需要通过 COpaquePointer 传递 StableRef

kotlin
import kotlinx.cinterop.*

class NativeDownloader(val onProgress: (Int) -> Unit) {

    fun start() {
        val selfPtr = StableRef.create(this).asCPointer()

        // 调用 C 函数,传入回调和自己的指针
        native_start_download(
            staticCFunction { progress, userPtr ->
                // 从指针还原 Kotlin 对象
                val instance = userPtr!!.asStableRef<NativeDownloader>().get()
                instance.onProgress(progress)
            },
            selfPtr
        )
    }
}

常用 C 类型与 Kotlin 映射进阶表

C 模式Kotlin 处理技巧
void *COpaquePointer?
char * (字符串)data.toKString() (注意编码)
int (*)(int)staticCFunction { ... }
opaque_handle_tCPointer<out CPointed>

内存泄漏的重灾区:StableRef 管理

极其重要

每一个 StableRef.create() 必须对应一个 dispose()。否则,即使 Kotlin 对象在逻辑上不再使用,它也会永远留在内存中,直到程序退出。

建议将 StableRef 的持有权绑定到具有生命周期的组件上,或者利用 try-finally 块确保释放。