mcpField
K
kuikly-expand-api

kuikly-expand-api

github.com/tencent-tds开发134441 次安装11 周前更新
代码仓库

关于 kuikly-expand-api

kuikly-expand-api 是面向 Kuikly 开发者的自定义 Module 开发指南,讲解 Kuikly 侧接口、Native 侧实现与注册,以及同步、异步和数据传输方式。

主要功能

  1. 01定义 Kuikly 侧 Module API
  2. 02实现并注册 Native 侧 Module
  3. 03同步与异步调用及回调
  4. 04支持多平台实现流程
  5. 05参数类型与序列化规则

适用场景

  1. 01将平台 API 暴露给 Kuikly 代码
  2. 02为目标平台实现自定义 Module
  3. 03在 Kuikly DSL 或 Compose DSL 中注册和调用 Module
  4. 04传输回调结果或二进制数据

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/tencent-tds/kuiklyui-ai --skill kuikly-expand-api

安装后在 Claude Code 中输入/kuikly-expand-api确认已加载。

附带文件references/MODULE_IMPLEMENT.md

详细介绍

来自项目 README

Kuikly 自定义 Module 开发

Contents

Core Guidelines

  • Module 是平台 API 的统一接口: Kuikly 本身不具备平台相关能力,通过 Module 机制将 Native API 暴露给 Kuikly 侧调用。
  • 自定义 Module 需双端实现: Kuikly 侧定义 API 接口,Native 侧(Android/iOS/鸿蒙/H5/小程序)实现具体逻辑,通过 moduleName 关联。
  • 通信方式分同步和异步: 同步调用(syncToNativeMethod)在 Kuikly 线程执行,异步调用(asyncToNativeMethod)在主线程执行。同步调用避免耗时操作。
  • Module 名字必须全端一致: Kuikly 侧 moduleName() 返回值必须与 Native 侧注册的名字完全一致。

Workflow: Creating Module (Kuikly Side)

Use this workflow to create a custom Module on the Kuikly (Kotlin) side.

步骤:

  1. 新建 Module 类继承 Module,实现 moduleName() 方法。
  2. 编写业务 API 方法,通过 toNative / syncToNativeMethod / asyncToNativeMethod 与 Native 通信。
  3. 在 Pager 的 createExternalModules() 中注册 Module。
  4. 在业务代码中通过 acquireModule 获取并使用。
Step 1: 定义 Module 类
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 方法

Module 提供以下通信方法:

方法调用方式Native 执行线程参数类型回调类型
syncToNativeMethod(methodName, JSONObject?, CallbackFn?)同步Kuikly 线程JSONObject(序列化为 JSON 字符串)CallbackFn(JSONObject?)
syncToNativeMethod(methodName, Array<Any>, AnyCallbackFn?)同步Kuikly 线程基本类型数组(String/Int/Float/ByteArray)AnyCallbackFn(Any?)
asyncToNativeMethod(methodName, JSONObject?, CallbackFn?)异步主线程JSONObject(序列化为 JSON 字符串)CallbackFn(JSONObject?)
asyncToNativeMethod(methodName, Array<Any>, AnyCallbackFn?)异步主线程基本类型数组(String/Int/Float/ByteArray)AnyCallbackFn(Any?)
toNative(keepCallbackAlive, methodName, param, callback, syncCall)通用底层方法取决于 syncCallAny?CallbackFn(JSONObject?)

异步调用(无返回值):

fun log(content: String) {
    asyncToNativeMethod(
        "log",
        JSONObject().apply { put("content", content) },
        null
    )
}

异步调用(带回调):

fun logWithCallback(content: String, callbackFn: CallbackFn) {
    asyncToNativeMethod(
        "logWithCallback",
        JSONObject().apply { put("content", content) },
        callbackFn
    )
}

同步调用(有返回值):

fun syncLog(content: String): String {
    return syncToNativeMethod(
        "syncLog",
        JSONObject().apply { put("content", content) },
        null
    )
}

二进制数据传输(使用 Array 参数):

fun uploadData(bytes: ByteArray): Any? {
    return syncToNativeMethod(
        "uploadData",
        arrayOf<Any>(bytes),
        null
    )
}

常驻回调(keepCallbackAlive = true):

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

自定义 Module 需要在 Pager 或 ComposeContainer 的 createExternalModules() 中注册,两种 DSL 的注册方式一致。

Kuikly DSL 注册:

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 注册:

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

Kuikly DSL 中使用:

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 中使用:

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

Use this workflow to implement the Native side of a custom Module.

前提: 只需在业务需要支持的平台上实现 Native 侧 Module,无需实现所有平台。

步骤:

  1. 确定需要支持的目标平台(Android/iOS/鸿蒙/H5/小程序)。
  2. 在目标平台宿主工程中创建对应的 Module 类。
  3. 实现 call 方法,根据 method 分发处理。
  4. 将 Module 注册到 Kuikly 框架。

💡 鸿蒙平台备注: 鸿蒙提供 ArkTS 和 C 两种实现方式,一般情况只需选择 ArkTS 实现即可。

各平台实现详见 MODULE_IMPLEMENT.md

速查:各平台 Module 基类与注册方式

平台基类注册方式注意事项
AndroidKuiklyRenderBaseModule()registerExternalModule 中 moduleExport(name) { Module() }重写 call(method, params: Any?, callback) 或 call(method, params: String?, callback)
iOSKRBaseModule类名必须与 Kuikly 侧 moduleName 一致(运行时动态创建)方法名与 Kuikly 侧 methodName 一致,参数固定为 NSDictionary,Swift 需 @objc 注解
鸿蒙 (ArkTS)KuiklyRenderBaseModulegetCustomRenderModuleCreatorRegisterMap 中注册需实现 syncMode() 指定同步/异步模式
鸿蒙 (C)KRRenderModuleRegisterV2InitKuikly 中注册返回值 KRAnyData 由框架释放
H5KuiklyRenderBaseModule()registerExternalModule 中 moduleExport(name) { Module() }同 Android
小程序KuiklyRenderBaseModule()registerExternalModule 中 moduleExport(name) { Module() }可通过 NativeApi.plat 访问 wx 对象
Native 侧支持的数据类型
平台支持的数据类型
AndroidString Int Long Float Double Boolean ByteArray Map List JSONObject
iOSNSString NSNumber BOOL NSData NSDictionary NSArray
鸿蒙String Int Long Float Double Bool ByteArray Array Map/Record
H5/小程序String Int Long Float Double Boolean Array Map List JSONObject JSONArray
数据序列化规则
类目序列化方式涉及类型
基础类型直接透传String Int Float Double Boolean NSNumber
二进制数据直接透传ByteArray NSData ArrayBuffer
JSON 数据JSON 字符串JSONObject JSONArray
集合类型JSON 字符串Map/Record List NSDictionary NSArray Array
特殊规则直接透传Array 中包含 ByteArray/NSData 时

⚠️ syncToNativeMethod 和 asyncToNativeMethod 传入 JSONObject 参数时会序列化为 JSON 字符串,不支持 ByteArray 二进制数据。传输二进制请使用 Array<Any> 参数的重载方法。


常见陷阱与正确做法

❌ 错误做法✅ 正确做法
Module 名字 Kuikly 侧与 Native 侧不一致确保 moduleName() 返回值与 Native 注册名完全一致
在 created 之前获取 Module在 created() 或之后获取 Module(Kuikly DSL);在 willInit 的 setContent 之后获取(Compose DSL)
同步调用中执行耗时操作同步调用在 Kuikly 线程执行,避免耗时操作阻塞渲染
忘记在 createExternalModules 中注册自定义 Module自定义 Module 必须在 Pager 的 createExternalModules() 中注册
用 JSONObject 参数传输二进制数据使用 Array<Any> 参数的 syncToNativeMethod/asyncToNativeMethod 传输 ByteArray
iOS 侧 Module 类名与 Kuikly 侧不一致iOS 通过类名动态创建 Module,类名必须与 moduleName() 一致
iOS 侧用 Swift 实现但未加 @objc 注解Swift 实现的 Module 需要 @objc 或 @objcMembers 修饰
常驻回调忘记手动移除keepCallbackAlive=true 的回调需在页面销毁前调用 removeCallback
Compose DSL 中直接调用 acquireModule在 @Composable 函数中需通过 LocalActivity.current.getPager().acquireModule 获取
Compose DSL 中忘记 import LocalActivity需 import com.tencent.kuikly.compose.ui.platform.LocalActivity

评论

支持 Markdown