RN通信原理 -- JavaScriptCore的应用

        在RN旧的版本中,JS与C++的通信,是借助于JSBridge 来完成的,而 JSBridge 实现的基础则是 RN 对JavaScriptCore做了一层封装,包括: JS与C++之间的方法、变量的转换。通信的介质则是 JSON配置数据,当然在两端还涉及JSON数据的序列化与反序列化。本篇文章中,我们暂不涉及其他方面的讲解,只针对 JavaScriptCore 的封装实现JS与C++数据交换 来讲解JS与C++实现通信的底层实现。

全篇文章多处用到 JavaScriptCore 当中的API,理解起来可能有点晦涩,不过没关系,大家感兴趣的话,可以查看它的官方文档 详细API看这里

一、JavaScriptCore API 数据结构:

数据类型描述
JSGlobalContextRefJavaScript全局上下文。也就是JavaScript的执行环境。
JSValueRef:JavaScript的一个值,可以是变量、object、函数。
JSObjectRef:JavaScript的一个object或函数。
JSStringRef:JavaScript的一个字符串。
JSClassRef:JavaScript的类。
JSClassDefinition:JavaScript的类定义,使用这个结构,C、C++可以定义和注入JavaScript的类。

二、JavaScriptCore API 主要函数:

API描述
JSGlobalContextCreate、JSGlobalContextRelease创建和销毁JavaScript全局上下文
JSContextGetGlobalObject:获取JavaScript的Global对象
JSObjectSetProperty、JSObjectGetPropertyJavaScript对象的属性操作
JSEvaluateScript执行一段JS脚本
JSClassCreate创建一个JavaScript类
JSObjectMake创建一个JavaScript对象
JSObjectCallAsFunction调用一个JavaScript函数
JSStringCreateWithUTF8Cstring、JSStringRelease创建、销毁一个JavaScript字符串
JSValueToBoolean、JSValueToNumber、JSValueToStringCopy、JSValueToObjectJSValueRef转为C++类型
JSValueMakeBoolean、JSValueMakeNumber、JSValueMakeStringC++类型转为JSValueRef

三、C++通过JSC调用JS

1. 创建JS执行环境

//创建JS全局上下文 (JS执行环境)
JSGlobalContextRef context = JSGlobalContextCreate(NULL);

2. 获取Global全局对象

//获取Global全局对象
JSObjectRef global = JSContextGetGlobalObject(context);

3. 取JS的全局变量、全局函数、全局复杂对象

/**
 * 获取JS的全局变量
 */

// 获取将要被调用的变量名,并转换为 JS 字符串
JSStringRef varName = JSStringCreateWithUTF8CString("JS 变量名");

// 从全局对象 global 中查找并获取 JS 全局变量 varName
JSValueRef var = JSObjectGetProperty(context, global, varName, NULL);

// 手动销毁变量名称字符串,释放内存
JSStringRelease(varName);

// 将JS变量转化为C++类型
int n = JSValueToNumber(context, var, NULL);



/**
 * 获取JS的全局函数
 */
//获取将要被调用的函数名,并转换为 JS 字符串
JSStringRef funcName = JSStringCreateWithUTF8CString("JS 函数名");

// 从全局对象 global 中查找并获取 JS 全局函数 funcName
JSValueRef func = JSObjectGetProperty(context, global, funcName, NULL);

// 手动销毁字符串,释放内存
JSStringRelease(funcName);

// 将JS函数转换为一个对象
JSObjectRef funcObject = JSValueToObject(context,func, NULL);

// 准备参数,将两个数值1和2作为两个参数
JSValueRef args[2];
args[0] = JSValueMakeNumber(context, 1);
args[1] = JSValueMakeNumber(context, 2);

// 调用JS函数,并接收返回值
JSValueRef returnValue = JSObjectCallAsFunction(context, funcObject, NULL, 2, args, NULL);

// 将JS的返回值转换为C++类型
int ret = JSValueToNumber(context, returnValue, NULL);

/**
 * 获取复杂的对象
 */
//获取将要被调用的JS对象名,并转换为 JS 字符串
JSStringRef objName = JSStringCreateWithUTF8CString("JS 对象名");

// 从全局对象 global 下的查找并生成一个对象
JSValueRef obj = JSObjectGetProperty(context, global, objName, NULL);

// 释放内存
JSStringRelease(objName);

// 将obj转换为对象类型
JSObjectRef object = JSValueToObject(context, obj, NULL);

// 获取JS复杂对象中的方法名
JSStringRef funcObjName = JSStringCreateWithUTF8CString("JS 对象中的方法名");

// 获取复杂对象中的函数
JSValueRef objFunc = JSObjectGetProperty(context, object, funcObjName, NULL);

//释放内存
JSStringRelease(funcObjName);

//调用复杂对象的方法, 这里省略了参数和返回值
JSObjectCallAsFunction(context, objFunc, NULL, 0, 0, NULL);

通过上面实现 C++ 调用 JS 的代码可知,在调用时,首先需要创建一个 JS 上下文环境,进而获取一个全局对象 global,所有会被C++调用的方法、变量,都会挂载到这个 global 对象上。我门通过 JavaScriptCore 提供的 API 做一系列的查询、类型转化,最终完成对JS的调用。

四、JS通过JSC调用C++

        JS想要调用C++,前提是必须先将 C++ 侧的变量、函数、类 通过类型转换之后 注入到JS中 ( 也就是需要将其作为属性设置到 全局对象 global 上 ),这样一来在JS侧就有了一份对照表,因此才能在 JS 侧发起调用流程。

        下面我们通过一个实例来了解一下,JS 是如何在 JSC 层完成对C++的调用的。首先我们定义一个C++ 类,并从中定义一组全局函数,然后封装 JavaScriptCore 对 C++ 类的调用,最后提供给JSC进行CallBack回调。

1. 定义一个 C++ 类

class test{

    public:
        test(){
            number = 0;
        };

        void func(){
            number++;
        }

        int number;

};

2. 定义一个变量 g_test

test g_test;

3. 封装对 test.func() 的调用

JSValueRef testFunc(JSContextRef ctx, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef*){
    test* t = static_cast<test*>(JSObjectGetPrivate(thisObject));
    t->func();
    return JSValueMakeUndefined(ctx);
}

4. 封装对 test.number 的 get 操作

JSValueRef getTestNumber(JSContextRef ctx, JSObjectRefthisObject, JSStringRef, JSValueRef*){
    test* t = static_cast<test*>(JSObjectGetPrivate(thisObject));
    return JSValueMakeNumber(ctx, t->number);
}

5. 编写一个方法来创建 JS 类对象

JSClassRef createTestClass(){
    //类的成员变量定义,可以有多个,最后一个必须是{ 0, 0, 0 },也可以指定set操作
    static JSStaticValue testValues[] = {
        {"number", getTestNumber, 0, kJSPropertyAttributeNone },
        { 0, 0, 0, 0}
    };

    //类的成员方法定义,可以有多个,最后一个必须是{ 0, 0, 0 }
    static JSStaticFunction testFunctions[] = {
        {"func", testFunc, kJSPropertyAttributeNone },
        { 0, 0, 0 }
    };

    //定义一个类,设置成员变量 和 成员方法
    static JSClassDefinition classDefinition = {
        0,kJSClassAttributeNone, "test", 0, testValues, testFunctions,
        0, 0, 0, 0,0, 0, 0, 0, 0, 0, 0
    };

    //创建一个 JS 类对象
    static JSClassRef t = JSClassCreate(&classDefinition);
    return t;
}

6. 将C++类转换后注入JS到全局对象 global

// 创建JS全局上下文 (JS执行环境)
JSGlobalContextRef ctx = JSGlobalContextCreate(NULL);

// 获取 global 全局对象
JSObjectRef globalObj = JSContextGetGlobalObject(ctx);

// 新建一个 JS 类对象,并使之绑定到 g_test 变量
JSObjectRef classObj = JSObjectMake(ctx, createTestClass(), &g_test);

// 获取将要被调用的 JS 对象名,并转换为 JS 字符串
JSStringRef objName = JSStringCreateWithUTF8CString("g_test");

// 将新建的 JS类对象 注入JS中( 即将 classObj 作为属性,挂载到全局对象 global 中 )
JSObjectSetProperty(ctx, globalObj, objName, classObj, kJSPropertyAttributeNone, NULL);

7. JS中完成调用

g_test.func();
let n = g_test.number;
let t = new test;

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值