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组件。

相关推荐
T^T尚1 小时前
uniapp H5上传图片前压缩
前端·javascript·uni-app
出逃日志1 小时前
JS的DOM操作和事件监听综合练习 (具备三种功能的轮播图案例)
开发语言·前端·javascript
XIE3921 小时前
如何开发一个脚手架
前端·javascript·git·npm·node.js·github
忘忧记2 小时前
Python MySQL SQLServer操作
android
GISer_Jing2 小时前
React渲染相关内容——渲染流程API、Fragment、渲染相关底层API
javascript·react.js·ecmascript
山猪打不过家猪2 小时前
React(五)——useContecxt/Reducer/useCallback/useRef/React.memo/useMemo
前端·javascript·react.js
前端青山2 小时前
React事件处理机制详解
开发语言·前端·javascript·react.js
万岳科技系统开发2 小时前
外卖系统开发实战:从架构设计到代码实现
android·小程序·开源
对卦卦上心2 小时前
React-useEffect的使用
前端·javascript·react.js
练习两年半的工程师2 小时前
React的基本知识:事件监听器、Props和State的区分、改变state的方法、使用回调函数改变state、使用三元运算符改变state
前端·javascript·react.js