我们游戏中,在之前是使用反射进行ts(js)<—>c++通信互调的,但是这样做的话,一开始还好,后来多了如官方文档所言,大量的反射有如灾难一样。于是,需要用到jsb绑定,参考了官方文档后,决定先尝试手动绑定,弄清原理:
Cocos Creator 版本:2.3.3
jsb_test_manual.cpp(参考了jsb_cocos2dx_audioengine_auto.cpp)
bool JSB_testBindCallback(se::State& s)
{
const auto& args = s.args();
size_t argc = args.size();
CC_UNUSED bool ok = true;
do {
if (argc < 0 || argc > 2)
{
break;
}
std::string json;
ok &= seval_to_std_string(args[0], &json);
SE_PRECONDITION2(ok, false, "JSB_testBindCallback arguments index: 0");
if (argc == 1)
{
GameNS::myCPPFunction(json, nullptr);
return true;
}
if (argc == 2)
{
GameNS::Callback func;
if (args[1].isObject() && args[1].toObject()->isFunction())
{
se::Value jsThis(s.thisObject());
se::Value jsFunc(args[1]);
jsFunc.toObject()->root();
auto lambda = [=](const std::string & ret) -> void
{
se::ScriptEngine::getInstance()->clearException();
se::AutoHandleScope hs;
CC_UNUSED bool ok = true;
se::ValueArray cb_args;
cb_args.resize(1);
ok &= std_string_to_seval(ret, &cb_args[0]);
if (ok)
{
se::Value rval;
se::Object *thisObj = jsThis.isObject() ? jsThis.toObject() : nullptr;
se::Object *funcObj = jsFunc.toObject();
bool succeed = funcObj->call(cb_args, thisObj, &rval);
if (!succeed)
{
se::ScriptEngine::getInstance()->clearException();
}
return;
}
};
func = lambda;
}
GameNS::myCPPFunction(json, func);
return true;
}
}while (false);
SE_REPORT_ERROR("JSB_testBindCallback: Invalid number of arguments");
return false;
}
SE_BIND_FUNC(JSB_testBindCallback)
test.h
namespace GameNS {
using Callback = std::function<void(const std::string&)>;
void myCPPFunction(std::string json, const Callback & callback);
}
test.cpp
Callback testCallback = nullptr;
void myCPPFunction(std::string json, const Callback & callback)
{
testCallback = callback;
auto obj = getInstanceObj();
JniHelper::callObjectVoidMethod(obj, "game/Test", "myJavaFunction", json);
}
extern "C" {
void Java_game_Test_onCallback(JNIEnv *env, jobject thiz, jstring string) {
if (GameNS::testCallback) {
GameNS::testCallback(JniHelper::jstring2string(string));
}
GameNS::testCallback = nullptr;
}
}
test.java
package game;
public class Test {
private Handler handler = new Handler(Looper.getMainLooper());
public static Test getInstance() {
if (instance == null) {
synchronized (Test.class) {
instance = new Test();
}
}
return instance;
}
public native void onCallback(String string);
public void myJavaFunction(String jsonStr) {
JSONObject json = null;
try {
json = new JSONObject(jsonStr);
} catch (JSONException e) {
e.printStackTrace();
}
handler.postDelayed(new Runnable() {
@Override
public void run() {
Log.d("Test", "handler post delayed");
onCallback("{}");
}
}, 1000);
}
}
ts调用
jsb.test.testBindCallback("{}", this.myTestMethod.bind(this));
目前有两个问题:
1)在JSB_testBindCallback中,如果se::AutoHandleScope hs;这一句,按我参考原有的绑定写法,是放到lambda中的,但是如果myJavaFunction中立即调用onCallback则完全没有问题,但是,如果handler延时模拟真实的异步回调,lambda在执行到se::AutoHandleScope hs;这一句时,就必然会报错,v8::Isolate::GetCurrent()是NULL,但是如果放到lambda外面则正常,这是为什么?
2)在JSB_testBindCallback中,我执行了一次jsFunc.toObject()->root();,是否需要在调用一次jsFunc.toObject()->unroot();?目前这样写是否会导致内存泄漏(尽管我看了文档对于root/unroot的描述,还是一头雾水)


