Call javascript from iOS/Android native with return value

I’m able to call js functions from C++ and Java without the return value but I don’t see how to get the value of the function back. Here’s what’s working just to call a function.

ScriptingCore* sc = ScriptingCore::getInstance();
if (sc) {
const char * strToEval = [expressionToEval cStringUsingEncoding:NSUTF8StringEncoding];
sc->evalString(strToEval);
}

Any thoughts?

C++ code (working):

bool execute_func()
{
    ScriptingCore* sc = ScriptingCore::getInstance();
    JSContext *cx = sc->getGlobalContext();
    JS::RootedObject global_obj(cx, sc->getGlobalObject());
    JS::RootedValue rval(cx);
    vector<JS::Value> arguments = {
        std_string_to_jsval(cx, "string"),
        JS::Int32Value(2019)
    };

    bool res = sc->executeFunctionWithOwner(OBJECT_TO_JSVAL(global_obj), "global_func_name",
        arguments.size(), const_cast<jsval*>(arguments.data()), &rval);

    if (res)
    {
        if (rval.isNullOrUndefined())
        {
            CCLOG("returned NullOrUndefined");
        }
        else if (rval.isBoolean())
        {
            CCLOG("returned bool: %d", rval.toBoolean());
        }
        else if (rval.isString())
        {
            string str;
            jsval_to_std_string(cx, rval, &str);
            CCLOG("returned string: %s", str.c_str());
        }
    }
    else
    {
        CCLOGERROR("failed call js function");
    }

    return res;
}

JS code:

function global_func_name(str, year) {
    cc.log("global_func_name " + str + ", " + year);
    return "Are you ready to meet New Year " + year + "?";
}
2 Likes

@dimon4eg, that worked great, thanks! Do you perhaps have a Java/Android version?

It’s C++ code and it’s cross platform

Hi @dimon4eg,

Can you tell me how to call JAVA/C++ functions from JS ?

I’m using cocos creator so I’m using purely JavaScript, When I build the project for android studio I got lots of c++ and java files, I need to call some JAVA functions based on in-game events like game over, level start etc…as the entire game is in JavaScript so I not able to find any way to call these functions.

Ok, here is example (not tested):

// this function will be called from js
bool function_called_from_js(JSContext *cx, uint32_t argc, jsval *vp)
{
    JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
    bool ok = true;

    // we expect only one argument
    if (argc == 1) {
        // ergument should be string
        if (!args.get(0).isString())
        {
            ok = false;
        }

        JSB_PRECONDITION2(ok, cx, false, "function_called_from_js : Error processing arguments");

        string argument;
        jsval_to_std_string(cx, args.get(0), &argument);

        CCLOG("function_called_from_js %s", argument.c_str());
        
        // here we call Java static function
        JniHelper::callStaticVoidMethod(
            "org/cocos2dx/lib/Cocos2dxHelper", // class package
            "call_from_cpp",                   // static function in Java
            "from C++"                         // string argument
        );

        args.rval().setUndefined();
        return true;
    }

    JS_ReportError(cx, "function_called_from_js : wrong number of arguments");
    return false;
}

void register_js_function(JSContext *cx, JS::HandleObject global)
{
    JS::RootedObject global_obj(cx, global);

    JS_DefineFunction(cx,
        global_obj,                // add function to global object
        "function_called_from_js", // function name
        function_called_from_js,   // C++ handler of js function
        1,                         // number of arguments
        JSPROP_READONLY | JSPROP_PERMANENT
    );
}

Add this to in AppDelegate.cpp:

// in AppDelegate.cpp add to bool AppDelegate::applicationDidFinishLaunching() method:
sc->addRegisterCallback(register_js_function);

Java code

// Java code of cocos\platform\android\java\src\org\cocos2dx\lib\Cocos2dxHelper.java
// native function declaration, it's implemented in C++ code
public static native void test_cpp_func(String str);

public static void call_from_cpp() {
    Log.d("Cocos2dxHelper", "Yahhoooo! Java code is executed!");
    test_cpp_func("Hello from Java!"); // call native C++ function
}

C++ function which will be called from Java

#if CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID

#include "platform/android/jni/JniHelper.h"

extern "C"{
    // full name is package path + function name
    JNIEXPORT void JNICALL Java_org_cocos2dx_lib_Cocos2dxHelper_test_cpp_func(JNIEnv *env, jobject thiz, jstring jstr) {
        std::string str = JniHelper::jstring2string(jstr);
        CCLOG("test_cpp_func: %s", str.c_str());
    }
}

#endif // CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID

JS test code:

function_called_from_js("test");

Flow:
from JS we call function_called_from_js, in C++ will be called bool function_called_from_js(JSContext *cx, uint32_t argc, jsval *vp); which calls Java call_from_cpp function which calls C++ test_cpp_func function and Java_org_cocos2dx_lib_Cocos2dxHelper_test_cpp_func will be called as last.

Thanks for your test code but I just found this solution on official documentation
http://docs.cocos2d-x.org/creator/manual/en/advanced-topics/java-reflection.html
It seems relatively easy to integrate

“Can you tell me how to call JAVA/C++ functions from JS ?”

I posted answer to your question.

Yes for just Java the jsb.reflection is simpler.

Thanks for your efforts I appreciate it, Your provided code will help me in someway…

1 Like