原生互操作深度实战
本章将带您进入 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_t | CPointer<out CPointed> |
内存泄漏的重灾区:StableRef 管理
极其重要
每一个 StableRef.create() 必须对应一个 dispose()。否则,即使 Kotlin 对象在逻辑上不再使用,它也会永远留在内存中,直到程序退出。
建议将 StableRef 的持有权绑定到具有生命周期的组件上,或者利用 try-finally 块确保释放。