kuikly-expand-api について
kuikly-expand-apiは、Kuikly開発者向けのカスタムModule開発ガイドです。Kuikly側APIの定義、ネイティブ側の実装と登録、同期・非同期呼び出しやデータ転送を扱います。
主な機能
- 01Kuikly側Module APIの定義
- 02ネイティブ側Moduleの実装と登録
- 03コールバック付き同期・非同期呼び出し
- 04複数プラットフォームでの実装手順
- 05パラメーター型とシリアライズ規則
活用シーン
- 01プラットフォームAPIをKuiklyコードに公開する
- 02対象プラットフォーム向けのModuleを実装する
- 03Kuikly DSLまたはCompose DSLでModuleを登録・呼び出す
- 04コールバック結果やバイナリデータを転送する
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ tencent-tds/ kuiklyui-ai --skill kuikly-expand-apiインストール後、Claude Code で/kuikly-expand-apiを入力して読み込まれたか確認します。
同梱ファイルreferences/MODULE_IMPLEMENT.md
詳細
Kuikly カスタム Module の開発
目次
コアガイドライン
- Module はプラットフォーム API の統一インターフェースです: Kuikly 自体はプラットフォーム固有の機能を持たず、Module の仕組みを通じて Native API を Kuikly 側に公開し、呼び出せるようにします。
- カスタム Module は両側で実装する必要があります: Kuikly 側で API インターフェースを定義し、Native 側(Android/iOS/鸿蒙/H5/小程序)で具体的なロジックを実装します。両者は
moduleNameで関連付けられます。 - 通信方式には同期と非同期があります: 同期呼び出し(
syncToNativeMethod)は Kuikly スレッドで実行され、非同期呼び出し(asyncToNativeMethod)はメインスレッドで実行されます。同期呼び出しでは時間のかかる処理を避けてください。 - Module 名はすべてのプラットフォームで一致させる必要があります: Kuikly 側の
moduleName()の戻り値は、Native 側で登録した名前と完全に一致している必要があります。
ワークフロー:Module の作成(Kuikly 側)
このワークフローでは、Kuikly(Kotlin)側でカスタム Module を作成します。
手順:
Moduleを継承する Module クラスを新規作成し、moduleName()メソッドを実装します。- ビジネス API メソッドを記述し、
toNative/syncToNativeMethod/asyncToNativeMethodを使って Native と通信します。 - Pager の
createExternalModules()で Module を登録します。 - ビジネスコードで
acquireModuleを使って取得し、使用します。
ステップ 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"
}
}
ステップ 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) | 汎用の低レベルメソッド | syncCall による | Any? | 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)
}
ステップ 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()
}
}
}
ステップ 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")
}
ワークフロー:Native 側 Module の実装
このワークフローでは、カスタム Module の Native 側を実装します。
前提条件: ビジネス上サポートが必要なプラットフォームでのみ、Native 側 Module を実装します。すべてのプラットフォームで実装する必要はありません。
手順:
- サポートする対象プラットフォーム(Android/iOS/鸿蒙/H5/小程序)を決定します。
- 対象プラットフォームのホストプロジェクトに、対応する Module クラスを作成します。
callメソッドを実装し、methodに基づいて処理を振り分けます。- Module を Kuikly フレームワークに登録します。
💡 鸿蒙プラットフォームに関する注記: 鸿蒙では ArkTS と C の 2 種類の実装方式が提供されています。通常は 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 側でサポートされるデータ型
| プラットフォーム | サポートされるデータ型 |
|---|---|
| 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>パラメーターを受け取るオーバーロードメソッドを使用してください。
よくある落とし穴と正しい対処法
| ❌ 誤った方法 | ✅ 正しい方法 |
|---|---|
| Module 名が Kuikly 側と Native 側で一致していない | moduleName() の戻り値を Native の登録名と完全に一致させてください |
created より前に Module を取得する | Kuikly DSL では created() 以降に Module を取得してください。Compose DSL では willInit の setContent の後に取得してください |
| 同期呼び出しで時間のかかる処理を実行する | 同期呼び出しは 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 で LocalActivity の import を忘れる | import com.tencent.kuikly.compose.ui.platform.LocalActivity が必要です |