Lua 与 ArkTS 交互 —— 优雅的 callStaticMethod
背景与问题
在游戏开发中,Lua 与 ArkTS 的交互随着接入 SDK 的 API 增加而变得频繁。传统方式下,每次新增接口都需要重新执行 Lua 绑定操作,这一过程繁琐且效率低下。基于以下现有技术:
-
Lua 与 C++ 交互可通过 tolua 绑定实现
-
C++ 与 ArkTS 交互可通过 NAPI 实现
为解决频繁绑定的问题,本文提供了一种改进方案,实现了类似 luaj.callStaticMethod 和 luaoc.callStaticMethod 的接口,减少重复的绑定操作。
实现方案
核心目标
实现两个关键接口,以支持 Lua 与 ArkTS 的灵活交互:
-
同步接口 luats.callStaticMethodSync:通过模板函数传递不同类型参数,并获取 ArkTS 不同类型的返回值给 Lua
-
异步接口 luats.callStaticMethodAsyn:利用定时器获取 ArkTS 异步结果,并自动回调给 Lua
核心代码(CCLuaArkTsBridge.cpp)
统一接口实现
// 统一接口:支持同步/异步调用,自动处理回调
// key: 例'OAuth.sendReq', params:例'{"key":"value"}', funcId:例200
void CCLuaArkTsBridge::callStaticMethodAsyn(const std::string key, const char *params /*= ""*/, int funcId /*= 0*/)
{
...
// 注册异步回调
if (funcId > 0)
{
_asyncInfoMap[key] = funcId;
if (_asyncInfoMap.size() == 1) { // 刚从0变为1
startSchedule();
}
// 异步调用:通过Js_Cocos2dxHelper传递key
JSFunction::getFunction(key).invokeAsync(key, params);
} else {
// 同步调用:无返回值,如果需要返回值使用callStaticMethodSync
JSFunction::getFunction(key).invoke<void>(params);
}
}
调度相关方法
// 启动调度(仅当未在调度时)
void CCLuaArkTsBridge::startSchedule() {
if (!_isScheduling && _scheduler) {
_scheduler->schedule(CC_SCHEDULE_SELECTOR(CCLuaArkTsBridge::checkResult), this, 0.0f, CC_REPEAT_FOREVER, 0.0f, false);
_isScheduling = true;
}
}
// 停止调度(仅当正在调度时)
void CCLuaArkTsBridge::stopSchedule() {
if (_isScheduling && _scheduler) {
_scheduler->unschedule(CC_SCHEDULE_SELECTOR(CCLuaArkTsBridge::checkResult), this);
_isScheduling = false;
}
}
void CCLuaArkTsBridge::checkResult(float /*dt*/) {
...
// 遍历所有注册的key
for (const auto& pair : _asyncInfoMap) {
const std::string& key = pair.first;
int funcId = pair.second;
// 从Js_Cocos2dxHelper获取结果
std::string result = Js_Cocos2dxHelper::getAsyncInfo(key);
if (!result.empty()) {
Js_Cocos2dxHelper::clearAsyncInfo(key); // 立即清除避免重复处理
// 回调到Lua
Director::getInstance()->getScheduler()->performFunctionInCocosThread([key, result, funcId]() {
LuaStack* luaStack = LuaEngine::getInstance()->getLuaStack();
luaStack->pushString(result.c_str());
int ret = luaStack->executeFunctionByHandler(funcId, 1);
});
_asyncInfoMap.erase(key);
if (_asyncInfoMap.empty()) {
stopSchedule(); // 若删除后_map为空,停止调度
}
}
}
}
实现原理
- 同步调用( luats.callStaticMethodSync ) :
-
利用模板函数处理不同类型的参数传递
-
自动获取 ArkTS 不同类型的返回值并传递给 Lua
// 模板同步调用:指定返回类型T,自动处理不同类型的返回值
template <typename T>
T callStaticMethodSync(const std::string key, const std::string params = "") {
try {
return JSFunction::getFunction(key).invoke<T>(params.c_str());
} catch (...) {
CCLOG("Sync call failed for key: %s, type mismatch", key.c_str());
return T();
}
}
- 异步调用( luats.callStaticMethodAsyn ) :
-
通过注册异步回调,将接口调用信息存储在 _asyncInfoMap 中
-
启动定时器定期检查异步调用结果
-
当获取到结果后,自动回调 Lua 函数处理结果
通过这种方式,无需频繁进行 Lua 绑定操作,即可实现 Lua 与 ArkTS 的灵活交互,提升了开发效率。