1 of 49

基于 Cinterop 和 KSP

搭建高效的鸿蒙跨语言通信框架

车林阳 快手基础架构团队

2 of 49

KMP 到鸿蒙

ArkTS <-> C <-> KMP 跨语言通信

高效的跨语言通信框架

快手自研 - 桥接代码自动生成工具

3 of 49

4 of 49

第一节

KMP 到鸿蒙

5 of 49

KMP

Kotlin Multiplatform

语言转译框架:将 Kotlin 代码编译成不同平台目标产物实现跨平台

6 of 49

FIR

Frontend intermediate representation

= syntax tree with semantic info

KotlinComplier

7 of 49

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

8 of 49

9 of 49

NAPI

实现 ArkTS 与 C 之间的相互调用

ArkTS

C / CPP

NAPI

Kotlin

so

10 of 49

KMP ↔ OHOS

11 of 49

KMP ↔ OHOS

12 of 49

KMP ↔ OHOS

13 of 49

第二节

跨语言通信 ArkTS ↔ C ↔ KMP

14 of 49

List

Kotlin

ArkTS

.so

Napi

Napi

调用 kmp so 得到 Kotlin List

napi_create_array 创建一个 ArkTS Array

将 Kotlin List 元素转换为

napi_value 并塞进 ArkTS Array

15 of 49

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

16 of 49

List

17 of 49

18 of 49

// 调用 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

19 of 49

KMP .h 中 Kotlin 类方法暴露不全,C 和 KMP 跨语言难度大

!

20 of 49

21 of 49

NAPI 桥接代码量大、冗余度高

!

22 of 49

第三节

构建高效的跨语言通信框架

23 of 49

AKI

NAPI 桥接代码自动生成框架�

极简使用,解耦FFI代码与业务代码,友好的边界性编程体验;

提供完整的数据类型转换、函数绑定、对象绑定、线程安全等特性;

支持JS & C/C++互调;

支持与Node-API嵌套使用;

24 of 49

Origin

AKI

25 of 49

OK

NAPI 桥接代码量大、冗余度高

KMP .h 中 Kotlin 类方法暴露不全,跨语言难度大

26 of 49

napi bridge

napi bridge

.h 中 Kotlin 类方法暴露不全

Kotlin 同语言

27 of 49

Cinterop

基于 .h 文件生成 Kotlin API

28 of 49

Cinterop

基于 .h 文件生成 Kotlin API

29 of 49

Cinterop

提供了 Kotlin 和 C 交互的方式

30 of 49

CPointer

C Pointer.

nativeHeap.alloc<CVariable>().ptr -> CPointer<CPointed>

31 of 49

CValue

The sequence of immutable C values.

cValue<CVariable>() -> CValue

32 of 49

Cinterop

33 of 49

xx.h

kt api

Cinterop

so1

so2

Kotlin 反向调用 C

XXX SDK

ArkTS

xx.c

34 of 49

napi bridge

.h 文件 Kotlin 类方法暴露不全

kotlin napi api

napi.h

cinterop

knapi bridge

Kotlin 同语言

35 of 49

NAPI 下沉

三层跨语言模型简化为两层跨语言模型�

Origin

Knapi 下沉

36 of 49

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

37 of 49

Origin

38 of 49

Knapi 下沉

39 of 49

OK

KMP .h 中 Kotlin 类方法暴露不全,跨语言难度大 - 彻底解决,极大减少代码量

NAPI 桥接代码量大、冗余度高

40 of 49

从 JS 环境获取上下文

遍历 n 个 Js 方法入参,

根据类型不同调用相应的方法将 Js Type -> Kotlin Type

调用 Kotlin Function

根据 return type 调用不同的方法将 Kotlin Type -> JsType 并 return

相同的是处理方式

不同的是参数和返回值

41 of 49

编译时访问和处理 Kotlin 代码符号(如类、函数、属性等)

在编译时获取 Kotlin 元数据,包括注解、类型信息、继承关系等

生成新的 Kotlin 代码文件

KSP

Kotlin Symbol Processing

42 of 49

第四节

快手自研 · 基于 KSP 的桥接代码自动生成工具

43 of 49

KNapiGetCallCbInfoCodeGen

KNapiParamGroupCodeGen

KNapiFunctionCallCodeGen

KNapiReturnCodeGen

Single Napi Method

44 of 49

1. 获得需要绑定的方法和类的类型信息

2. 自动生成 Kotlin NAPI Bridge 代码

3. 完成 Napi Descriptor 注册

Kotlin Type -> JS Type

JS Type -> Kotlin Type

Function Invoker

Napi Register

45 of 49

快手 BridgeGen 框架

46 of 49

Origin

KwaiGen

47 of 49

快手基础架构团队长期招聘高级架构师

欢迎投递简历 chelinyang@kuaishou.com

48 of 49

49 of 49

The End

Thanks