ReactNative JSI(4)

ReactNative JSI

简述

React Native使用JavaScript来实现UI,新架构使用的jsi来实现C/C++和JavaScript之间的互相调用,而jsi是使用JS引擎提供的api能力来实现的,如果看过前面TurboModule的介绍,应该对JSI有一些概念了,这一节我们会更加进一步去介绍JSI的实现,其实JSI的逻辑是很简单的,只不过因为有非常多的数据类型,所以有许多的业务逻辑。

Runtime

JSI定义了一个Runtime作为接口,不同的JS引擎都需要实现这个Runtime,JavaScriptCore的实现是JSCRuntime,而Hermes的实现是HermesRuntime,HermesRuntime的源码没有放在RN里,我们只要知道这个Runtime的实现类里就是通过JS引擎提供的api来实现Runtime定义的接口功能,这里就可以解决JSI接口固定,但是底层使用不同的JS引擎的情况。

我们来看看Runtime定义的一些接口。

这里提供了大量接口,执行JavaScript代码,创建JS对象,获得或者配置JS对象的属性,数据类型的转换等等,根据不同的JS引擎提供的api来实现这些方法,然后JSI就是基于这些接口来做封装。

class JSI_EXPORT Runtime {
public:
virtual ~Runtime();

// 执行JavaScript代码,这个我们之前启动流程中看到有使用过
virtual Value evaluateJavaScript(
    const std::shared_ptr<const Buffer>& buffer,
    const std::string& sourceURL) = 0;

// 检查,预处理,JavaScript代码
virtual std::shared_ptr<const PreparedJavaScript> prepareJavaScript(
    const std::shared_ptr<const Buffer>& buffer,
    std::string sourceURL) = 0;

// 执行已经预处理的JavaScript代码
virtual Value evaluatePreparedJavaScript(
    const std::shared_ptr<const PreparedJavaScript>& js) = 0;

// 将微任务挂到队列上
virtual void queueMicrotask(const jsi::Function& callback) = 0;

// 清理微任务
virtual bool drainMicrotasks(int maxMicrotasksHint = -1) = 0;

// 获取global对象
virtual Object global() = 0;

// ...

protected:
// 这些类映射JavaScript上的一些内容
friend class Pointer;
friend class PropNameID;
friend class Symbol;
friend class BigInt;
friend class String;
friend class Object;
friend class WeakObject;
friend class Array;
friend class ArrayBuffer;
friend class Function;
friend class Value;
friend class Scope;
friend class JSError;

struct PointerValue {
    virtual void invalidate() = 0;

protected:
    virtual ~PointerValue() = default;
};

// 一些clone函数
virtual PointerValue* cloneSymbol(const Runtime::PointerValue* pv) = 0;
virtual PointerValue* cloneBigInt(const Runtime::PointerValue* pv) = 0;
virtual PointerValue* cloneString(const Runtime::PointerValue* pv) = 0;
virtual PointerValue* cloneObject(const Runtime::PointerValue* pv) = 0;
virtual PointerValue* clonePropNameID(const Runtime::PointerValue* pv) = 0;
// 构建PropNameID,JavaScript对象里的变量在这里是以键值对的形式,PropNameID去关联属性。  
virtual PropNameID createPropNameIDFromAscii(
    const char* str,
    size_t length) = 0;
virtual PropNameID createPropNameIDFromUtf8(
    const uint8_t* utf8,
    size_t length) = 0;
virtual PropNameID createPropNameIDFromString(const String& str) = 0;
virtual PropNameID createPropNameIDFromSymbol(const Symbol& sym) = 0;
virtual std::string utf8(const PropNameID&) = 0;
virtual bool compare(const PropNameID&, const PropNameID&) = 0;

// 限免是一些数据类型的相互转换
virtual std::string symbolToString(const Symbol&) = 0;

virtual BigInt createBigIntFromInt64(int64_t) = 0;
virtual BigInt createBigIntFromUint64(uint64_t) = 0;
virtual bool bigintIsInt64(const BigInt&) = 0;
virtual bool bigintIsUint64(const BigInt&) = 0;
virtual uint64_t truncate(const BigInt&) = 0;
virtual String bigintToString(const BigInt&, int) = 0;

virtual String createStringFromAscii(const char* str, size_t length) = 0;
virtual String createStringFromUtf8(const uint8_t* utf8, size_t length) = 0;
virtual std::string utf8(const String&) = 0;

virtual Value createValueFromJsonUtf8(const uint8_t* json, size_t length);
// 构建JS对象的方法
virtual Object createObject() = 0;
virtual Object createObject(std::shared_ptr<HostObject> ho) = 0;
virtual std::shared_ptr<HostObject> getHostObject(const jsi::Object&) = 0;
virtual HostFunctionType& getHostFunction(const jsi::Function&) = 0;

virtual bool hasNativeState(const jsi::Object&) = 0;
virtual std::shared_ptr<NativeState> getNativeState(const jsi::Object&) = 0;
virtual void setNativeState(
    const jsi::Object&,
    std::shared_ptr<NativeState> state) = 0;
// 获取或者设置属性
virtual Value getProperty(const Object&, const PropNameID& name) = 0;
virtual Value getProperty(const Object&, const String& name) = 0;
virtual bool hasProperty(const Object&, const PropNameID& name) = 0;
virtual bool hasProperty(const Object&, const String& name) = 0;
virtual void setPropertyValue(
    const Object&,
    const PropNameID& name,
    const Value& value) = 0;
virtual void
setPropertyValue(const Object&, const String& name, const Value& value) = 0;
// 判断数据类型
virtual bool isArray(const Object&) const = 0;
virtual bool isArrayBuffer(const Object&) const = 0;
virtual bool isFunction(const Object&) const = 0;
virtual bool isHostObject(const jsi::Object&) const = 0;
virtual bool isHostFunction(const jsi::Function&) const = 0;
virtual Array getPropertyNames(const Object&) = 0;

virtual WeakObject createWeakObject(const Object&) = 0;
virtual Value lockWeakObject(const WeakObject&) = 0;

virtual Array createArray(size_t length) = 0;
virtual ArrayBuffer createArrayBuffer(
    std::shared_ptr<MutableBuffer> buffer) = 0;
virtual size_t size(const Array&) = 0;
virtual size_t size(const ArrayBuffer&) = 0;
virtual uint8_t* data(const ArrayBuffer&) = 0;
virtual Value getValueAtIndex(const Array&, size_t i) = 0;
virtual void
setValueAtIndexImpl(const Array&, size_t i, const Value& value) = 0;
// 创建方法
virtual Function createFunctionFromHostFunction(
    const PropNameID& name,
    unsigned int paramCount,
    HostFunctionType func) = 0;
virtual Value call(
    const Function&,
    const Value& jsThis,
    const Value* args,
    size_t count) = 0;
virtual Value
callAsConstructor(const Function&, const Value* args, size_t count) = 0;

// Private data for managing scopes.
struct ScopeState;
virtual ScopeState* pushScope();
virtual void popScope(ScopeState*);

virtual bool strictEquals(const Symbol& a, const Symbol& b) const = 0;
virtual bool strictEquals(const BigInt& a, const BigInt& b) const = 0;
virtual bool strictEquals(const String& a, const String& b) const = 0;
virtual bool strictEquals(const Object& a, const Object& b) const = 0;

virtual bool instanceOf(const Object& o, const Function& f) = 0;

virtual void setExternalMemoryPressure(
    const jsi::Object& obj,
    size_t amount) = 0;

template <typename T>
static T make(PointerValue* pv);
static PointerValue* getPointerValue(Pointer& pointer);
static const PointerValue* getPointerValue(const Pointer& pointer);
static const PointerValue* getPointerValue(const Value& value);

friend class ::FBJSRuntime;
template <typename Plain, typename Base>
friend class RuntimeDecorator;
};

JSI使用

我们以上一章提到过的TurboModule来作为例子,来介绍怎么JSI是怎么使用的。

我们之前介绍过TurboModule是继承于jni::HostObject,HostObject是可以在JS运行时添加到JS引擎中的对象,我们先看看HostObject的定义。

class JSI_EXPORT HostObject {
    public:
    virtual ~HostObject();

    // 根据PropNameID获取对应的Value,JS调用对象的对应属性就会通过这里来获取
    virtual Value get(Runtime&, const PropNameID& name);
    // 配置PropNameID对应的Value
    virtual void set(Runtime&, const PropNameID& name, const Value& value);
    // 当JS层需要获取一个属性列表,会调用该方法。
    virtual std::vector<PropNameID> getPropertyNames(Runtime& rt);
};

再来看看TurboModule是怎么实现这几个方法。

先看看get方法:

facebook::jsi::Value get(
    facebook::jsi::Runtime& runtime,
    const facebook::jsi::PropNameID& propName) override {
    {
        // 调用create来获取对应propName的Vaule
        auto prop = create(runtime, propName);
        if (jsRepresentation_ && !prop.isUndefined()) {
            jsRepresentation_->lock(runtime).asObject(runtime).setProperty(
                runtime, propName, prop);
        }
        return prop;
    }
}

create方法通过methodMap_来查找是否存在对应的属性,TurboModule里面全部都是方法,我们在上一节看过,脚手架生成的代码会把定义的方法都存储在这个methodMap_里面。

这里获取到方法后需要将它封装成jsi::Function才能返回给JSI。JSI只能接收这些JSI自己定义的类型。

virtual jsi::Value create(
    jsi::Runtime& runtime,
    const jsi::PropNameID& propName) {
    std::string propNameUtf8 = propName.utf8(runtime);
    // 在methodMap_中根据propName找对应的方法
    if (auto methodIter = methodMap_.find(propNameUtf8);
        methodIter != methodMap_.end()) {
        const MethodMetadata& meta = methodIter->second;
        // 找到的方法封装成jsi::Function返回
        return jsi::Function::createFromHostFunction(
            runtime,
            propName,
            static_cast<unsigned int>(meta.argCount),
            [this, meta](
                jsi::Runtime& rt,
                [[maybe_unused]] const jsi::Value& thisVal,
                const jsi::Value* args,
                size_t count) { return meta.invoker(rt, *this, args, count); });
        } else if (auto eventEmitterIter = eventEmitterMap_.find(propNameUtf8);
                eventEmitterIter != eventEmitterMap_.end()) {
        return eventEmitterIter->second->get(runtime, jsInvoker_);
    }
    // Neither Method nor EventEmitter were not found, let JS decide what to do.
    return facebook::jsi::Value::undefined();
}

再来看看getPropertyNames,这个很简单,只要把methodMap_的所有key都收集起来返回就可以了。

virtual std::vector<facebook::jsi::PropNameID> getPropertyNames(
    facebook::jsi::Runtime& runtime) override {
    std::vector<jsi::PropNameID> result;
    result.reserve(methodMap_.size());
    for (auto it = methodMap_.cbegin(); it != methodMap_.cend(); ++it) {
        result.push_back(jsi::PropNameID::forUtf8(runtime, it->first));
    }
    return result;
}

TurboModule并没有实现set方法,因为这些方法不应该在JS可以修改,所以这里就没有实现。

小结

本节介绍了jsi::Runtime的定义的一些接口,然后以TurboModule为例介绍了JSI应该如何使用,总体来说还是比较简单的,下一节我们再来看一下Fabric渲染器和Fabric组件。

相关推荐
Monly214 分钟前
JS:JSON操作
前端·javascript·json
Patience to do12 分钟前
Android Studio项目(算法计算器)
android·算法·android studio
觉醒法师1 小时前
HarmonyOS开发 - 本地持久化之实现LocalStorage支持多实例
前端·javascript·华为·typescript·harmonyos
w风雨无阻w2 小时前
Vue3 学习笔记(十一)Vue生命周期
javascript·vue.js·前端框架·vue3
清清ww2 小时前
【vue】13.深入理解递归组件
前端·javascript·vue.js
清清ww2 小时前
【vue】09.computer和watch的使用
前端·javascript·vue.js
你不讲 wood2 小时前
使用 Axios 上传大文件分片上传
开发语言·前端·javascript·node.js·html·html5
我又来搬代码了3 小时前
【Android】使用TextView实现按钮开关代替Switch开关
android
清灵xmf4 小时前
UI 组件的二次封装
前端·javascript·vue.js·element-plus
x原力觉醒5 小时前
uniapp跨域问题,在开发环境中配置
javascript·vue.js·uni-app