kuikly-expand-api
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseKuikly 自定义 Module 开发
Kuikly 自定义 Module 开发
Contents
目录
Core Guidelines
核心准则
- Module 是平台 API 的统一接口: Kuikly 本身不具备平台相关能力,通过 Module 机制将 Native API 暴露给 Kuikly 侧调用。
- 自定义 Module 需双端实现: Kuikly 侧定义 API 接口,Native 侧(Android/iOS/鸿蒙/H5/小程序)实现具体逻辑,通过 关联。
moduleName - 通信方式分同步和异步: 同步调用()在 Kuikly 线程执行,异步调用(
syncToNativeMethod)在主线程执行。同步调用避免耗时操作。asyncToNativeMethod - Module 名字必须全端一致: Kuikly 侧 返回值必须与 Native 侧注册的名字完全一致。
moduleName()
- Module 是平台API的统一接口: Kuikly本身不具备平台相关能力,通过Module机制将Native API暴露给Kuikly侧调用。
- 自定义Module需双端实现: Kuikly侧定义API接口,Native侧(Android/iOS/鸿蒙/H5/小程序)实现具体逻辑,通过关联。
moduleName - 通信方式分同步和异步: 同步调用()在Kuikly线程执行,异步调用(
syncToNativeMethod)在主线程执行。同步调用避免耗时操作。asyncToNativeMethod - Module名字必须全端一致: Kuikly侧返回值必须与Native侧注册的名字完全一致。
moduleName()
Workflow: Creating Module (Kuikly Side)
工作流程:创建自定义Module(Kuikly侧)
Use this workflow to create a custom Module on the Kuikly (Kotlin) side.
步骤:
- 新建 Module 类继承 ,实现
Module方法。moduleName() - 编写业务 API 方法,通过 /
toNative/syncToNativeMethod与 Native 通信。asyncToNativeMethod - 在 Pager 的 中注册 Module。
createExternalModules() - 在业务代码中通过 获取并使用。
acquireModule
使用以下流程在Kuikly(Kotlin)侧创建自定义Module。
步骤:
- 新建Module类继承,实现
Module方法。moduleName() - 编写业务API方法,通过/
toNative/syncToNativeMethod与Native通信。asyncToNativeMethod - 在Pager的中注册Module。
createExternalModules() - 在业务代码中通过获取并使用。
acquireModule
Step 1: 定义 Module 类
Step 1: 定义Module类
kotlin
import com.tencent.kuikly.core.module.Module
import com.tencent.kuikly.core.module.CallbackFn
import com.tencent.kuikly.core.nvi.serialization.json.JSONObject
class MyLogModule : Module() {
override fun moduleName(): String = "KRMyLogModule"
companion object {
const val MODULE_NAME = "KRMyLogModule"
}
}kotlin
import com.tencent.kuikly.core.module.Module
import com.tencent.kuikly.core.module.CallbackFn
import com.tencent.kuikly.core.nvi.serialization.json.JSONObject
class MyLogModule : Module() {
override fun moduleName(): String = "KRMyLogModule"
companion object {
const val MODULE_NAME = "KRMyLogModule"
}
}Step 2: 编写 API 方法
Step 2: 编写API方法
Module 提供以下通信方法:
| 方法 | 调用方式 | Native 执行线程 | 参数类型 | 回调类型 |
|---|---|---|---|---|
| 同步 | Kuikly 线程 | JSONObject(序列化为 JSON 字符串) | |
| 同步 | Kuikly 线程 | 基本类型数组(String/Int/Float/ByteArray) | |
| 异步 | 主线程 | JSONObject(序列化为 JSON 字符串) | |
| 异步 | 主线程 | 基本类型数组(String/Int/Float/ByteArray) | |
| 通用底层方法 | 取决于 syncCall | Any? | |
异步调用(无返回值):
kotlin
fun log(content: String) {
asyncToNativeMethod(
"log",
JSONObject().apply { put("content", content) },
null
)
}异步调用(带回调):
kotlin
fun logWithCallback(content: String, callbackFn: CallbackFn) {
asyncToNativeMethod(
"logWithCallback",
JSONObject().apply { put("content", content) },
callbackFn
)
}同步调用(有返回值):
kotlin
fun syncLog(content: String): String {
return syncToNativeMethod(
"syncLog",
JSONObject().apply { put("content", content) },
null
)
}二进制数据传输(使用 Array<Any> 参数):
kotlin
fun uploadData(bytes: ByteArray): Any? {
return syncToNativeMethod(
"uploadData",
arrayOf<Any>(bytes),
null
)
}常驻回调(keepCallbackAlive = true):
kotlin
fun registerListener(callbackFn: CallbackFn): CallbackRef {
val result = toNative(
true, // keepCallbackAlive: callback 不会被自动销毁
"registerListener",
null,
callbackFn,
false
)
return result.callbackRef!!
}
// 手动移除回调
fun unregisterListener(callbackRef: CallbackRef) {
removeCallback(callbackRef)
}Module提供以下通信方法:
| 方法 | 调用方式 | Native执行线程 | 参数类型 | 回调类型 |
|---|---|---|---|---|
| 同步 | Kuikly线程 | JSONObject(序列化为JSON字符串) | |
| 同步 | Kuikly线程 | 基本类型数组(String/Int/Float/ByteArray) | |
| 异步 | 主线程 | JSONObject(序列化为JSON字符串) | |
| 异步 | 主线程 | 基本类型数组(String/Int/Float/ByteArray) | |
| 通用底层方法 | 取决于syncCall | Any? | |
异步调用(无返回值):
kotlin
fun log(content: String) {
asyncToNativeMethod(
"log",
JSONObject().apply { put("content", content) },
null
)
}异步调用(带回调):
kotlin
fun logWithCallback(content: String, callbackFn: CallbackFn) {
asyncToNativeMethod(
"logWithCallback",
JSONObject().apply { put("content", content) },
callbackFn
)
}同步调用(有返回值):
kotlin
fun syncLog(content: String): String {
return syncToNativeMethod(
"syncLog",
JSONObject().apply { put("content", content) },
null
)
}二进制数据传输(使用Array<Any>参数):
kotlin
fun uploadData(bytes: ByteArray): Any? {
return syncToNativeMethod(
"uploadData",
arrayOf<Any>(bytes),
null
)
}常驻回调(keepCallbackAlive = true):
kotlin
fun registerListener(callbackFn: CallbackFn): CallbackRef {
val result = toNative(
true, // keepCallbackAlive: callback不会被自动销毁
"registerListener",
null,
callbackFn,
false
)
return result.callbackRef!!
}
// 手动移除回调
fun unregisterListener(callbackRef: CallbackRef) {
removeCallback(callbackRef)
}Step 3: 注册 Module
Step 3: 注册Module
自定义 Module 需要在 Pager 或 ComposeContainer 的 中注册,两种 DSL 的注册方式一致。
createExternalModules()Kuikly DSL 注册:
kotlin
import com.tencent.kuikly.core.annotations.Page
import com.tencent.kuikly.core.module.Module
@Page("MyPage")
internal class MyPage : Pager() {
override fun createExternalModules(): Map<String, Module>? {
return mapOf(
MyLogModule.MODULE_NAME to MyLogModule()
)
}
// 如果有基类 BasePager 已注册了其他 Module,需要合并:
// override fun createExternalModules(): Map<String, Module>? {
// val modules = (super.createExternalModules() as? HashMap) ?: hashMapOf()
// modules[MyLogModule.MODULE_NAME] = MyLogModule()
// return modules
// }
override fun body(): ViewBuilder { ... }
}Compose DSL 注册:
kotlin
import com.tencent.kuikly.compose.ComposeContainer
import com.tencent.kuikly.compose.setContent
import com.tencent.kuikly.core.annotations.Page
import com.tencent.kuikly.core.module.Module
@Page("MyComposePage")
class MyComposePage : ComposeContainer() {
// 注册方式与 Kuikly DSL 完全一致
override fun createExternalModules(): Map<String, Module>? {
return mapOf(
MyLogModule.MODULE_NAME to MyLogModule()
)
}
override fun willInit() {
super.willInit()
setContent {
MyScreen()
}
}
}自定义Module需要在Pager或ComposeContainer的中注册,两种DSL的注册方式一致。
createExternalModules()Kuikly DSL注册:
kotlin
import com.tencent.kuikly.core.annotations.Page
import com.tencent.kuikly.core.module.Module
@Page("MyPage")
internal class MyPage : Pager() {
override fun createExternalModules(): Map<String, Module>? {
return mapOf(
MyLogModule.MODULE_NAME to MyLogModule()
)
}
// 如果有基类BasePager已注册了其他Module,需要合并:
// override fun createExternalModules(): Map<String, Module>? {
// val modules = (super.createExternalModules() as? HashMap) ?: hashMapOf()
// modules[MyLogModule.MODULE_NAME] = MyLogModule()
// return modules
// }
override fun body(): ViewBuilder { ... }
}Compose DSL注册:
kotlin
import com.tencent.kuikly.compose.ComposeContainer
import com.tencent.kuikly.compose.setContent
import com.tencent.kuikly.core.annotations.Page
import com.tencent.kuikly.core.module.Module
@Page("MyComposePage")
class MyComposePage : ComposeContainer() {
// 注册方式与Kuikly DSL完全一致
override fun createExternalModules(): Map<String, Module>? {
return mapOf(
MyLogModule.MODULE_NAME to MyLogModule()
)
}
override fun willInit() {
super.willInit()
setContent {
MyScreen()
}
}
}Step 4: 使用 Module
Step 4: 使用Module
Kuikly DSL 中使用:
kotlin
override fun created() {
super.created()
val myLogModule = acquireModule<MyLogModule>(MyLogModule.MODULE_NAME)
// 异步调用
myLogModule.log("test log")
// 异步调用带回调
myLogModule.logWithCallback("log with callback") { data ->
val result = data // Native 侧返回的 JSONObject
}
// 同步调用
val result = myLogModule.syncLog("sync log")
}Compose DSL 中使用:
kotlin
import androidx.compose.runtime.Composable
import com.tencent.kuikly.compose.ui.platform.LocalActivity
import com.tencent.kuikly.core.pager.Pager
@Composable
fun MyScreen() {
val pager = LocalActivity.current.getPager() as Pager
val myLogModule = pager.acquireModule<MyLogModule>(MyLogModule.MODULE_NAME)
// 异步调用
myLogModule.log("test log")
// 异步调用带回调
myLogModule.logWithCallback("log with callback") { data ->
val result = data // Native 侧返回的 JSONObject
}
// 同步调用
val result = myLogModule.syncLog("sync log")
}Kuikly DSL中使用:
kotlin
override fun created() {
super.created()
val myLogModule = acquireModule<MyLogModule>(MyLogModule.MODULE_NAME)
// 异步调用
myLogModule.log("test log")
// 异步调用带回调
myLogModule.logWithCallback("log with callback") { data ->
val result = data // Native侧返回的JSONObject
}
// 同步调用
val result = myLogModule.syncLog("sync log")
}Compose DSL中使用:
kotlin
import androidx.compose.runtime.Composable
import com.tencent.kuikly.compose.ui.platform.LocalActivity
import com.tencent.kuikly.core.pager.Pager
@Composable
fun MyScreen() {
val pager = LocalActivity.current.getPager() as Pager
val myLogModule = pager.acquireModule<MyLogModule>(MyLogModule.MODULE_NAME)
// 异步调用
myLogModule.log("test log")
// 异步调用带回调
myLogModule.logWithCallback("log with callback") { data ->
val result = data // Native侧返回的JSONObject
}
// 同步调用
val result = myLogModule.syncLog("sync log")
}Workflow: Implementing Native Side Module
工作流程:实现原生侧Module
Use this workflow to implement the Native side of a custom Module.
前提: 只需在业务需要支持的平台上实现 Native 侧 Module,无需实现所有平台。
步骤:
- 确定需要支持的目标平台(Android/iOS/鸿蒙/H5/小程序)。
- 在目标平台宿主工程中创建对应的 Module 类。
- 实现 方法,根据
call分发处理。method - 将 Module 注册到 Kuikly 框架。
💡 鸿蒙平台备注: 鸿蒙提供 ArkTS 和 C 两种实现方式,一般情况只需选择 ArkTS 实现即可。
各平台实现详见 MODULE_IMPLEMENT.md
速查:各平台 Module 基类与注册方式
| 平台 | 基类 | 注册方式 | 注意事项 |
|---|---|---|---|
| Android | | | 重写 |
| iOS | | 类名必须与 Kuikly 侧 moduleName 一致(运行时动态创建) | 方法名与 Kuikly 侧 methodName 一致,参数固定为 |
| 鸿蒙 (ArkTS) | | | 需实现 |
| 鸿蒙 (C) | | | 返回值 KRAnyData 由框架释放 |
| H5 | | | 同 Android |
| 小程序 | | | 可通过 |
使用以下流程实现自定义Module的原生侧逻辑。
前提: 只需在业务需要支持的平台上实现Native侧Module,无需实现所有平台。
步骤:
- 确定需要支持的目标平台(Android/iOS/鸿蒙/H5/小程序)。
- 在目标平台宿主工程中创建对应的Module类。
- 实现方法,根据
call分发处理。method - 将Module注册到Kuikly框架。
💡 鸿蒙平台备注: 鸿蒙提供ArkTS和C两种实现方式,一般情况只需选择ArkTS实现即可。
各平台实现详见 MODULE_IMPLEMENT.md
速查:各平台Module基类与注册方式
| 平台 | 基类 | 注册方式 | 注意事项 |
|---|---|---|---|
| Android | | | 重写 |
| iOS | | 类名必须与Kuikly侧moduleName一致(运行时动态创建) | 方法名与Kuikly侧methodName一致,参数固定为 |
| 鸿蒙 (ArkTS) | | | 需实现 |
| 鸿蒙 (C) | | | 返回值KRAnyData由框架释放 |
| H5 | | | 同Android |
| 小程序 | | | 可通过 |
Native 侧支持的数据类型
Native侧支持的数据类型
| 平台 | 支持的数据类型 |
|---|---|
| Android | |
| iOS | |
| 鸿蒙 | |
| H5/小程序 | |
| 平台 | 支持的数据类型 |
|---|---|
| Android | |
| iOS | |
| 鸿蒙 | |
| H5/小程序 | |
数据序列化规则
数据序列化规则
| 类目 | 序列化方式 | 涉及类型 |
|---|---|---|
| 基础类型 | 直接透传 | |
| 二进制数据 | 直接透传 | |
| JSON 数据 | JSON 字符串 | |
| 集合类型 | JSON 字符串 | |
| 特殊规则 | 直接透传 | Array 中包含 |
⚠️和syncToNativeMethod传入 JSONObject 参数时会序列化为 JSON 字符串,不支持 ByteArray 二进制数据。传输二进制请使用asyncToNativeMethod参数的重载方法。Array<Any>
| 类目 | 序列化方式 | 涉及类型 |
|---|---|---|
| 基础类型 | 直接透传 | |
| 二进制数据 | 直接透传 | |
| JSON数据 | JSON字符串 | |
| 集合类型 | JSON字符串 | |
| 特殊规则 | 直接透传 | Array中包含 |
⚠️和syncToNativeMethod传入JSONObject参数时会序列化为JSON字符串,不支持ByteArray二进制数据。传输二进制请使用asyncToNativeMethod参数的重载方法。Array<Any>
常见陷阱与正确做法
常见陷阱与正确做法
| ❌ 错误做法 | ✅ 正确做法 |
|---|---|
| Module 名字 Kuikly 侧与 Native 侧不一致 | 确保 |
在 | 在 |
| 同步调用中执行耗时操作 | 同步调用在 Kuikly 线程执行,避免耗时操作阻塞渲染 |
忘记在 | 自定义 Module 必须在 Pager 的 |
| 用 JSONObject 参数传输二进制数据 | 使用 |
| iOS 侧 Module 类名与 Kuikly 侧不一致 | iOS 通过类名动态创建 Module,类名必须与 |
iOS 侧用 Swift 实现但未加 | Swift 实现的 Module 需要 |
| 常驻回调忘记手动移除 | |
Compose DSL 中直接调用 | 在 |
Compose DSL 中忘记 import | 需 |
| ❌ 错误做法 | ✅ 正确做法 |
|---|---|
| Module名字Kuikly侧与Native侧不一致 | 确保 |
在 | 在 |
| 同步调用中执行耗时操作 | 同步调用在Kuikly线程执行,避免耗时操作阻塞渲染 |
忘记在 | 自定义Module必须在Pager的 |
| 用JSONObject参数传输二进制数据 | 使用 |
| iOS侧Module类名与Kuikly侧不一致 | iOS通过类名动态创建Module,类名必须与 |
iOS侧用Swift实现但未加 | Swift实现的Module需要 |
| 常驻回调忘记手动移除 | |
Compose DSL中直接调用 | 在 |
Compose DSL中忘记import | 需 |