kuikly-expand-api

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Kuikly 自定义 Module 开发

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 侧注册的名字完全一致。

  • 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)

工作流程:创建自定义Module(Kuikly侧)

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
    获取并使用。
使用以下流程在Kuikly(Kotlin)侧创建自定义Module。
步骤:
  1. 新建Module类继承
    Module
    ,实现
    moduleName()
    方法。
  2. 编写业务API方法,通过
    toNative
    /
    syncToNativeMethod
    /
    asyncToNativeMethod
    与Native通信。
  3. 在Pager的
    createExternalModules()
    中注册Module。
  4. 在业务代码中通过
    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 执行线程参数类型回调类型
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?)
异步调用(无返回值):
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执行线程参数类型回调类型
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?)
异步调用(无返回值):
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 的
createExternalModules()
中注册,两种 DSL 的注册方式一致。
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的
createExternalModules()
中注册,两种DSL的注册方式一致。
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,无需实现所有平台。
步骤:
  1. 确定需要支持的目标平台(Android/iOS/鸿蒙/H5/小程序)。
  2. 在目标平台宿主工程中创建对应的 Module 类。
  3. 实现
    call
    方法,根据
    method
    分发处理。
  4. 将 Module 注册到 Kuikly 框架。
💡 鸿蒙平台备注: 鸿蒙提供 ArkTS 和 C 两种实现方式,一般情况只需选择 ArkTS 实现即可。
各平台实现详见 MODULE_IMPLEMENT.md
速查:各平台 Module 基类与注册方式
平台基类注册方式注意事项
Android
KuiklyRenderBaseModule()
registerExternalModule
moduleExport(name) { Module() }
重写
call(method, params: Any?, callback)
call(method, params: String?, callback)
iOS
KRBaseModule
类名必须与 Kuikly 侧 moduleName 一致(运行时动态创建)方法名与 Kuikly 侧 methodName 一致,参数固定为
NSDictionary
,Swift 需
@objc
注解
鸿蒙 (ArkTS)
KuiklyRenderBaseModule
getCustomRenderModuleCreatorRegisterMap
中注册
需实现
syncMode()
指定同步/异步模式
鸿蒙 (C)
KRRenderModuleRegisterV2
InitKuikly
中注册
返回值 KRAnyData 由框架释放
H5
KuiklyRenderBaseModule()
registerExternalModule
moduleExport(name) { Module() }
同 Android
小程序
KuiklyRenderBaseModule()
registerExternalModule
moduleExport(name) { Module() }
可通过
NativeApi.plat
访问
wx
对象
使用以下流程实现自定义Module的原生侧逻辑。
前提: 只需在业务需要支持的平台上实现Native侧Module,无需实现所有平台。
步骤:
  1. 确定需要支持的目标平台(Android/iOS/鸿蒙/H5/小程序)。
  2. 在目标平台宿主工程中创建对应的Module类。
  3. 实现
    call
    方法,根据
    method
    分发处理。
  4. 将Module注册到Kuikly框架。
💡 鸿蒙平台备注: 鸿蒙提供ArkTS和C两种实现方式,一般情况只需选择ArkTS实现即可。
各平台实现详见 MODULE_IMPLEMENT.md
速查:各平台Module基类与注册方式
平台基类注册方式注意事项
Android
KuiklyRenderBaseModule()
registerExternalModule
moduleExport(name) { Module() }
重写
call(method, params: Any?, callback)
call(method, params: String?, callback)
iOS
KRBaseModule
类名必须与Kuikly侧moduleName一致(运行时动态创建)方法名与Kuikly侧methodName一致,参数固定为
NSDictionary
,Swift需
@objc
注解
鸿蒙 (ArkTS)
KuiklyRenderBaseModule
getCustomRenderModuleCreatorRegisterMap
中注册
需实现
syncMode()
指定同步/异步模式
鸿蒙 (C)
KRRenderModuleRegisterV2
InitKuikly
中注册
返回值KRAnyData由框架释放
H5
KuiklyRenderBaseModule()
registerExternalModule
moduleExport(name) { Module() }
同Android
小程序
KuiklyRenderBaseModule()
registerExternalModule
moduleExport(name) { Module() }
可通过
NativeApi.plat
访问
wx
对象

Native 侧支持的数据类型

Native侧支持的数据类型

平台支持的数据类型
Android
String
Int
Long
Float
Double
Boolean
ByteArray
Map
List
JSONObject
iOS
NSString
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
平台支持的数据类型
Android
String
Int
Long
Float
Double
Boolean
ByteArray
Map
List
JSONObject
iOS
NSString
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>
参数的重载方法。

类目序列化方式涉及类型
基础类型直接透传
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

❌ 错误做法✅ 正确做法
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