基于 Cinterop 和 KSP
搭建高效的鸿蒙跨语言通信框架
车林阳 快手基础架构团队
KMP 到鸿蒙
ArkTS <-> C <-> KMP 跨语言通信
高效的跨语言通信框架
快手自研 - 桥接代码自动生成工具
第一节
KMP 到鸿蒙
KMP
Kotlin Multiplatform
语言转译框架:将 Kotlin 代码编译成不同平台目标产物实现跨平台
FIR
Frontend intermediate representation
= syntax tree with semantic info
KotlinComplier
fun main() {
println("Hello, World!")
}
MODULE_FRAGMENT name:<main>
FILE fqName:<root> fileName: .../main.kt
FUN name:main visibility:public modality:FINAL <> () returnType:kotlin.Unit
BLOCK_BODY
CALL 'public final fun println (message: kotlin.Any?): kotlin.Unit [inline] declared in kotlin.io.ConsoleKt' type=kotlin.Unit origin=null
message: CONST String type=kotlin.String value="Hello, World!"
KotlinComplier
NAPI
实现 ArkTS 与 C 之间的相互调用�
ArkTS
�
C / CPP
NAPI
Kotlin
so
KMP ↔ OHOS
KMP ↔ OHOS
KMP ↔ OHOS
第二节
跨语言通信 ArkTS ↔ C ↔ KMP
List
Kotlin
ArkTS
.so
Napi
Napi
调用 kmp so 得到 Kotlin List
napi_create_array 创建一个 ArkTS Array
将 Kotlin List 元素转换为
napi_value 并塞进 ArkTS Array
define internal nonnull %struct.ObjHeader* @"kfun:com.kwai.kmp.napi.generator.TestList#testList(){}kotlin.collections.MutableList<kotlin.Int>"(%struct.ObjHeader* nocapture readnone %0, %struct.ObjHeader** nocapture %1) #4 {
prologue:
...
%7 = call %struct.ObjHeader* @"kfun:kotlin.collections#mutableListOf(kotlin.Array<out|0:0>...){0\C2\A7<kotlin.Any?>}kotlin.collections.MutableList<0:0>"(%struct.ObjHeader* %6, %struct.ObjHeader** %5)
...
ret %struct.ObjHeader* %7
}
define internal nonnull %struct.ObjHeader* @"kfun:kotlin.collections#mutableListOf(kotlin.Array<out|0:0>...){0\C2\A7<kotlin.Any?>}kotlin.collections.MutableList<0:0>"(%struct.ObjHeader* %0, %struct.ObjHeader** nocapture %1) #4 {
prologue:
...
%7 = call %struct.ObjHeader* @AllocInstance(%struct.TypeInfo* @"kclass:kotlin.collections.ArrayList", %struct.ObjHeader** %1) #75
ret %struct.ObjHeader* %7
}
@"ktypeglobal:kotlin.collections.ArrayList#internal" = internal constant { %struct.TypeInfo, [27 x i8*] } {
%struct.TypeInfo { ... },
[27 x i8*] [
i8* bitcast (i32 (%struct.ObjHeader*)* @"kfun:kotlin.collections.ArrayList#<get-size>(){}kotlin.Int" to i8*),
i8* bitcast (i1 (%struct.ObjHeader*, %struct.ObjHeader*)* @"kfun:kotlin.collections.AbstractMutableList#contains(1:0){}kotlin.Boolean" to i8*),
i8* bitcast (i1 (%struct.ObjHeader*, %struct.ObjHeader*)* @"kfun:kotlin.collections.AbstractCollection#containsAll(kotlin.collections.Collection<1:0>){}kotlin.Boolean" to i8*),
i8* bitcast (i1 (%struct.ObjHeader*)* @"kfun:kotlin.collections.ArrayList#isEmpty(){}kotlin.Boolean" to i8*),
i8* bitcast (%struct.ObjHeader* (%struct.ObjHeader*, %struct.ObjHeader**)* @"kfun:kotlin.collections.ArrayList#iterator(){}kotlin.collections.MutableIterator<1:0>" to i8*),
...] }
查看
LLVM IR
List
List
// 调用 napi_function
void CppCallback(int value) {
// 1. 将 int 转化为 napi_value
napi_create_int32(env, value, argv)
// 2. 调用 napi_function
napi_call_function(env, nullptr, &func, 1, argv);
}
static napi_value NapiMethod(napi_env env, napi_callback_info info) {
// 1. get_cb_info 拿到 ArkTS 定义的 callback,是一个 napi_function
napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr);
// 2. 拿到 napi_function 入参全局保存
func = &argv[0];
// 3. 调用 Kotlin 侧的方法
dynamic_symbols()->kotlin.ktTestCallback({(void*) CppCallback});
}
Kotlin
ArkTS
.so
Napi
Napi
Callback
KMP .h 中 Kotlin 类方法暴露不全,C 和 KMP 跨语言难度大
!
NAPI 桥接代码量大、冗余度高
!
第三节
构建高效的跨语言通信框架
AKI
NAPI 桥接代码自动生成框架�
极简使用,解耦FFI代码与业务代码,友好的边界性编程体验;
提供完整的数据类型转换、函数绑定、对象绑定、线程安全等特性;
支持JS & C/C++互调;
支持与Node-API嵌套使用;
Origin
AKI
OK
NAPI 桥接代码量大、冗余度高
KMP .h 中 Kotlin 类方法暴露不全,跨语言难度大
napi bridge
napi bridge
.h 中 Kotlin 类方法暴露不全
Kotlin 同语言
Cinterop
基于 .h 文件生成 Kotlin API
�
Cinterop
基于 .h 文件生成 Kotlin API
�
Cinterop
提供了 Kotlin 和 C 交互的方式
CPointer
C Pointer.
nativeHeap.alloc<CVariable>().ptr -> CPointer<CPointed>
CValue
The sequence of immutable C values.
cValue<CVariable>() -> CValue
Cinterop
xx.h
kt api
Cinterop
so1
so2
Kotlin 反向调用 C
XXX SDK
ArkTS
xx.c
napi bridge
.h 文件 Kotlin 类方法暴露不全
kotlin napi api
napi.h
cinterop
knapi bridge
Kotlin 同语言
NAPI 下沉
三层跨语言模型简化为两层跨语言模型�
Origin
Knapi 下沉
ArkTS
C mmkv
Kotlin SDK
使用 napi 绑定 JS 方法和 C 方法
napi_value methos(napi_env, napi_callback_info) {
// 1. 调用 kmp .h中提供的方法,得到 void*
// 2. 按照 ir 解析 void* 内存布局找到函数指针
// 3. 调用对应函数得到 result
// 4. result -> napi_result
}
so1
napi.so
napi.h
kt napi api
cinterop
使用 kotlin napi api 绑定 JS 方法和 kotlin 方法
napi_value methos(napi_env, napi_callback_info) {
// 1. 直接调用 kotlin method 得到结果
// 2. 调用 kt napi api 将 result -> napi_result
}
List
bridge.so
so1
napi.so
Origin
Knapi 下沉
OK
KMP .h 中 Kotlin 类方法暴露不全,跨语言难度大 - 彻底解决,极大减少代码量
NAPI 桥接代码量大、冗余度高
从 JS 环境获取上下文
遍历 n 个 Js 方法入参,
根据类型不同调用相应的方法将 Js Type -> Kotlin Type
调用 Kotlin Function
根据 return type 调用不同的方法将 Kotlin Type -> JsType 并 return
相同的是处理方式
不同的是参数和返回值
编译时访问和处理 Kotlin 代码符号(如类、函数、属性等)
在编译时获取 Kotlin 元数据,包括注解、类型信息、继承关系等
生成新的 Kotlin 代码文件
KSP
Kotlin Symbol Processing�
第四节
快手自研 · 基于 KSP 的桥接代码自动生成工具
KNapiGetCallCbInfoCodeGen
KNapiParamGroupCodeGen
KNapiFunctionCallCodeGen
KNapiReturnCodeGen
Single Napi Method
1. 获得需要绑定的方法和类的类型信息
2. 自动生成 Kotlin NAPI Bridge 代码
3. 完成 Napi Descriptor 注册
Kotlin Type -> JS Type
JS Type -> Kotlin Type
Function Invoker
Napi Register
快手 BridgeGen 框架
Origin
KwaiGen
快手基础架构团队长期招聘高级架构师
�欢迎投递简历 chelinyang@kuaishou.com
The End
Thanks