ReactNative 통신 메커니즘c++ 엔 드 소스 분석

21051 단어
ReactNative: 소스 코드 분석 시리즈
ReactNative 시작 과정 소스 코드 분석 ReactNative 통신 메커니즘자바 엔 드 소스 분석 ReactNative 통신 메커니즘c++ 엔 드 소스 분석 ReactNative 통신 메커니즘js 엔 드 소스 분석
지난 장 에서 우 리 는 자바 엔 드 통신 메커니즘 절 차 를 분 석 했 는데 이 장 에서 우 리 는 c + 엔 드 호출 절 차 를 분석 했다.
CatalystInstanceImpl.cpp
Catalyst InstanceImpl. 자바 류 에 대응 하여 자바 엔 드 native 방법의 구체 적 인 실현 입 니 다.
void CatalystInstanceImpl::initializeBridge(
    jni::alias_ref<:javaobject> callback,
    // This executor is actually a factory holder.
    JavaScriptExecutorHolder* jseh,
    jni::alias_ref<:javaobject> jsQueue,
    jni::alias_ref<:javaobject> nativeModulesQueue,
    jni::alias_ref<:javaobject> uiBackgroundQueue,
    jni::alias_ref
        <:jcollection>::javaobject> javaModules,
    jni::alias_ref
        <:jcollection>::javaobject> cxxModules) {
  moduleMessageQueue_ = std::make_shared(nativeModulesQueue);
  if (uiBackgroundQueue.get() != nullptr) {
    uiBackgroundMessageQueue_ = 
      std::make_shared(uiBackgroundQueue);
  }

  moduleRegistry_ = std::make_shared(
    buildNativeModuleList(
       std::weak_ptr(instance_),
       javaModules,
       cxxModules,
       moduleMessageQueue_,
       uiBackgroundMessageQueue_));

  instance_->initializeBridge(
    folly::make_unique(
    callback,
    uiBackgroundMessageQueue_ != 
          NULL ? uiBackgroundMessageQueue_ : moduleMessageQueue_),
    jseh->getExecutorFactory(),
    folly::make_unique(jsQueue),
    moduleRegistry_);
}
  • ModuleRegistry 인 스 턴 스 를 만 듭 니 다. buildNativeModuleList 는 ModuleRegistry Builder. cpp 의 방법 으로 자바 Modules 와 cxx Modules 의 자바 인 스 턴 스 집합 을 c + 의 NativeModule 인 스 턴 스 집합 으로 변환 합 니 다.2. instance 호출의 initializeBridge 방법.instance_인 스 턴 스. cpp 의 인 스 턴 스 입 니 다.
  • void CatalystInstanceImpl::jniCallJSFunction(std::string module, 
            std::string method, NativeArray* arguments) {
      instance_->callJSFunction(std::move(module),
                                std::move(method),
                                arguments->consume());
    }
    

    그냥 리 턴 instance대응 방법.
    void CatalystInstanceImpl::jniCallJSCallback(jint callbackId,
             NativeArray* arguments) {
      instance_->callJSCallback(callbackId, arguments->consume());
    } 
    

    마찬가지 로 반전 instance대응 방법.
    ModuleRegistry.cpp
    NativeModule. cpp 목록 modules 저장,자바 엔 드 를 되 돌 리 는 NativeModule 방법 입 니 다.
    folly::Optional ModuleRegistry::getConfig(const std::string& name) {
            ``````
    }
    

    주로 NativeModule 의 방법 명 목록 과 상수 목록 을 얻어 js 단 NativeModules. js 에 전달 합 니 다.
    void ModuleRegistry::callNativeMethod(unsigned int moduleId, 
             unsigned int methodId, folly::dynamic&& params, int callId) {
      if (moduleId >= modules_.size()) {
        throw std::runtime_error(
          folly::to<:string>("moduleId ", moduleId,
             " out of range [0..", modules_.size(), ")"));
      }
      modules_[moduleId]->invoke(methodId, std::move(params), callId);
    }
    

    비동기 NativeModule 방법의 호출자바 모듈 러 Wrapper. cpp 의 invoke 방법 을 호출 하면 자바 모듈 러 Wrapper. 자바 의 invoke 방법 을 호출 합 니 다.
    MethodCallResult ModuleRegistry::callSerializableNativeHook(unsigned int moduleId,
            unsigned int methodId, folly::dynamic&& params) {
      if (moduleId >= modules_.size()) {
        throw std::runtime_error(
          folly::to<:string>("moduleId ", moduleId,
            "out of range [0..", modules_.size(), ")"));
      }
      return modules_[moduleId]->
           callSerializableNativeHook(methodId, std::move(params));
    }
    

    NativeModule 방법의 호출 을 동기 화 합 니 다.자바 모듈 러 Wrapper. cpp 의 callSerializable NativeHook 방법 을 호출 합 니 다. 이 방법 은 NativeModule 방법 을 직접 호출 하여 결과 값 을 얻 고 되 돌려 줍 니 다.
    JavaModuleWrapper.cpp
    void JavaNativeModule::invoke(unsigned int reactMethodId, 
             folly::dynamic&& params, int callId) {
      messageQueueThread_->runOnQueue(
             [this, reactMethodId, params=std::move(params), callId] {
        static auto invokeMethod = wrapper_->getClass()->getMethod
                   ("invoke");
        #ifdef WITH_FBSYSTRACE
        if (callId != -1) {
          fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId);
        }
        #endif
        invokeMethod(
          wrapper_,
          static_cast(reactMethodId),
          ReadableNativeArray::newObjectCxxArgs(std::move(params)).get());
      });
    }
    

    이 방법 은 주로 자바 모듈 러 Wrapper. 자바 의 invoke 방법 을 호출 합 니 다.wrapper_자바 모듈 러 Wrapper. 자바 에 대응 하 는 실례 입 니 다.
    MethodCallResult JavaNativeModule::callSerializableNativeHook(
               unsigned int reactMethodId, folly::dynamic&& params) {
      // TODO: evaluate whether calling through invoke is potentially faster
      if (reactMethodId >= syncMethods_.size()) {
        throw std::invalid_argument(
          folly::to<:string>("methodId ", reactMethodId,
               " out of range [0..", syncMethods_.size(), "]"));
      }
    
      auto& method = syncMethods_[reactMethodId];
      CHECK(method.hasValue() && method->isSyncHook()) 
             << "Trying to invoke a asynchronous method as synchronous hook";
      return method->invoke(instance_, wrapper_->getModule(), params);
    }
    

    NativeModule 방법 을 직접 호출 하고 결과 값 을 되 돌려 줍 니 다.
    Instance.cpp
    주요 역할 은 Catalyst InstanceImpl 에 대응 하 는 방법 을 처리 하고 NativeToJSBridge 인 스 턴 스 를 만 드 는 것 입 니 다.
    void Instance::initializeBridge(
        std::unique_ptr callback,
        std::shared_ptr jsef,
        std::shared_ptr jsQueue,
        std::shared_ptr moduleRegistry) {
      callback_ = std::move(callback);
      moduleRegistry_ = std::move(moduleRegistry);
    
      jsQueue->runOnQueueSync([this, &jsef, jsQueue]() mutable {
        nativeToJsBridge_ = folly::make_unique(
            jsef.get(), moduleRegistry_, jsQueue, callback_);
    
        std::lock_guard<:mutex> lock(m_syncMutex);
        m_syncReady = true;
        m_syncCV.notify_all();
      });
    
      CHECK(nativeToJsBridge_);
    }
    

    속성 값 을 할당 하고 NativeToJSBridge 인 스 턴 스 를 만 듭 니 다.callback_자바 엔 드 ReactCallback 의 인 스 턴 스 입 니 다. jsef 는 JSCExecutor. cpp 인 스 턴 스 를 생 성 하 는 공장 류 를 얻 을 수 있 습 니 다.
    void Instance::callJSFunction(std::string &&module, std::string &&method,
                                  folly::dynamic &&params) {
      callback_->incrementPendingJSCalls();
      nativeToJsBridge_->callFunction(std::move(module), std::move(method),
                                      std::move(params));
    }
    

    먼저 자바 엔 드 ReactCallback 의 increment PendingJScalls 방법 을 바 꾼 다음 nativeToJs Bridge 를 호출 합 니 다.의 대응 방법.
    void Instance::callJSCallback(uint64_t callbackId, folly::dynamic &&params) {
      SystraceSection s("Instance::callJSCallback");
      callback_->incrementPendingJSCalls();
      nativeToJsBridge_->invokeCallback((double)callbackId, std::move(params));
    }
    

    먼저 자바 엔 드 ReactCallback 의 increment PendingJScalls 방법 을 바 꾼 다음 nativeToJs Bridge 를 호출 합 니 다.의 대응 방법.
    NativeToJsBridge.cpp
    JSToNativeBridge 인 스 턴 스 와 JSexecutor 인 스 턴 스 (즉 JSCExecutor. cpp 인 스 턴 스) 를 만 든 다음 JSexecutor 대응 방법 으로 js 와 상호작용 합 니 다.이 클래스 는 JsToNativeBridge 클래스 도 포함 되 어 있 습 니 다. 자바 엔 드 와 상호작용 을 하 는 것, 즉 NativeModule 을 호출 하 는 것 입 니 다. 모든 모듈 레지스터 리 를 가지 고 있 습 니 다.
    JsToNativeBridge
     JsToNativeBridge(std::shared_ptr registry,
                       std::shared_ptr callback)
        : m_registry(registry)
        , m_callback(callback) {}
    

    모듈 레지스터 리 속성 을 가 져 옵 니 다.콜백 은 자바 엔 드 의 BridgeCallback 인 스 턴 스 입 니 다.
    void callNativeModules(
          JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) override {
    
        CHECK(m_registry || calls.empty()) <<
          "native module calls cannot be completed with no native modules";
        m_batchHadNativeModuleCalls = m_batchHadNativeModuleCalls || !calls.empty();
    
        for (auto& call : parseMethodCalls(std::move(calls))) {
          m_registry->callNativeMethod(call.moduleId, call.methodId, std::move(call.arguments), call.callId);
        }
        if (isEndOfBatch) {
          if (m_batchHadNativeModuleCalls) {
            m_callback->onBatchComplete();
            m_batchHadNativeModuleCalls = false;
          }
          m_callback->decrementPendingJSCalls();
        }
      }
    

    우 리 는 m 를 통 해registry 의 callNativeMethod 방법 은 자바 단 에 대응 하 는 비동기 방법 을 되 돌려 줍 니 다.parseMethodCalls 는 MethodCall. cpp 의 방법 으로 js 단 에서 전 달 된 json 형식 데 이 터 를 우리 가 필요 로 하 는 데이터 형식 으로 변환 하 는 것 이 주요 역할 을 합 니 다.
     MethodCallResult callSerializableNativeHook(
          JSExecutor& executor, unsigned int moduleId, unsigned int methodId,
          folly::dynamic&& args) override {
        return m_registry->callSerializableNativeHook(moduleId, methodId, std::move(args));
      }
    

    통과 mregistry 의 callSerializable NativeHook 은 NativeModule 동기 화 방법 을 호출 합 니 다.
    다음은 NativeToJSBridge 의 방법 입 니 다.
    NativeToJsBridge::NativeToJsBridge(
        JSExecutorFactory* jsExecutorFactory,
        std::shared_ptr registry,
        std::shared_ptr jsQueue,
        std::shared_ptr callback)
        : m_destroyed(std::make_shared(false))
        , m_delegate(std::make_shared(registry, callback))
        , m_executor(jsExecutorFactory->createJSExecutor(m_delegate, jsQueue))
        , m_executorMessageQueueThread(std::move(jsQueue)) {}
    

    JSToNativeBridge 인 스 턴 스 m 을 만 든 것 을 볼 수 있 습 니 다.delegate, 그리고 JSexecutor 인 스 턴 스 mexecutor (즉 JSCExecutor. cpp 의 인 스 턴 스).
    void NativeToJsBridge::callFunction(
        std::string&& module,
        std::string&& method,
        folly::dynamic&& arguments) {
      int systraceCookie = -1;
      #ifdef WITH_FBSYSTRACE
      systraceCookie = m_systraceCookie++;
      FbSystraceAsyncFlow::begin(
          TRACE_TAG_REACT_CXX_BRIDGE,
          "JSCall",
          systraceCookie);
      #endif
    
      runOnExecutorQueue([module = std::move(module), method = std::move(method), 
                arguments = std::move(arguments), systraceCookie]
        (JSExecutor* executor) {
          #ifdef WITH_FBSYSTRACE
          FbSystraceAsyncFlow::end(
              TRACE_TAG_REACT_CXX_BRIDGE,
              "JSCall",
              systraceCookie);
          SystraceSection s("NativeToJsBridge::callFunction", 
               "module", module, "method", method);
          #endif
        
          executor->callFunction(module, method, arguments);
        });
    }
    

    JSexecutor 의 callFunction 방법 을 사용 합 니 다.
    void NativeToJsBridge::invokeCallback(double callbackId,
             folly::dynamic&& arguments) {
      int systraceCookie = -1;
      #ifdef WITH_FBSYSTRACE
      systraceCookie = m_systraceCookie++;
      FbSystraceAsyncFlow::begin(
          TRACE_TAG_REACT_CXX_BRIDGE,
          "",
          systraceCookie);
      #endif
    
      runOnExecutorQueue(
           [callbackId, arguments = std::move(arguments), systraceCookie]
        (JSExecutor* executor) {
          #ifdef WITH_FBSYSTRACE
          FbSystraceAsyncFlow::end(
              TRACE_TAG_REACT_CXX_BRIDGE,
              "",
              systraceCookie);
          SystraceSection s("NativeToJsBridge::invokeCallback");
          #endif
          executor->invokeCallback(callbackId, arguments);
        });
    }
    

    JSexecutor 의 invokeCallback 방법 (JSC Executor. cpp 에서 구체 적 으로 구현) 을 사용 합 니 다.
    JSCExecutor.cpp
    진짜 js 단 에서 상호작용 을 하고 js 단 을 직접 호출 하 는 방법 입 니 다.
    std::unique_ptr JSCExecutorFactory::createJSExecutor(
          std::shared_ptr delegate, 
          std::shared_ptr jsQueue) {
          return folly::make_unique(delegate, jsQueue, m_jscConfig);
        }
    

    JSexecutor 인 스 턴 스 를 만 들 고 JSToNativeBridge 인 스 턴 스 대상 delegate 를 가지 고 자바 엔 드 방법 을 되 돌 릴 수 있 습 니 다.
    void JSCExecutor::bindBridge() throw(JSException) {
          SystraceSection s("JSCExecutor::bindBridge");
          std::call_once(m_bindFlag, [this] {
            auto global = Object::getGlobalObject(m_context);
            auto batchedBridgeValue = global.getProperty("__fbBatchedBridge");
            if (batchedBridgeValue.isUndefined()) {
              auto requireBatchedBridge = 
                   global.getProperty("__fbRequireBatchedBridge");
              if (!requireBatchedBridge.isUndefined()) {
                batchedBridgeValue = 
                   requireBatchedBridge.asObject().callAsFunction({});
              }
              if (batchedBridgeValue.isUndefined()) {
                throw JSException("Could not get BatchedBridge,
                   make sure your bundle is packaged correctly");
              }
            }
    
            auto batchedBridge = batchedBridgeValue.asObject();
            m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
            m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty("invokeCallbackAndReturnFlushedQueue").asObject();
            m_flushedQueueJS = batchedBridge.getProperty("flushedQueue").asObject();
            m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnResultAndFlushedQueue").asObject();
          });
        }
    

    이 방법 은 통신 다 리 를 구축 하여 js 엔 드 의 인 스 턴 스 대상 과 방법 을 얻 고 js 엔 드 코드 를 직접 호출 합 니 다. 'fbBatched Bridge ': Batched Bridge. js 에서 정 의 된 것 입 니 다. 사실은 Message Queue. js 대상 인 스 턴 스 입 니 다. 네 가지 방법 을 참조 하여 js 방법 을 직접 호출 합 니 다.
  • m_callFunction Return Flushed QueueJS: js 단 JavaScriptModule 대응 방법 을 호출 하고 호출 된 결과 값 을 받 지 않 습 니 다.
  • m_invokeCallbackAndReturnFlushed QueueJS: NativeModule 비동기 방법 으로 결과 값 을 되 돌려 줍 니 다.
  • m_flushed QueueJS: js 단 에서 시작 하 는 NativeModule 비동기 방법 요청 목록 을 가 져 옵 니 다.
  • m_callFunction Return ResultAndFlushed QueueJS: mcallFunction Return Flushed QueueJS 는 같 지만 js 단 에서 호출 된 결과 값 을 받 아들 일 수 있 습 니 다.안 타 깝 게 도 이 방법 은 자바 단 에 의 해 호출 된 적 이 없다.
  • void JSCExecutor::callFunction(const std::string& moduleId, 
           const std::string& methodId, const folly::dynamic& arguments) {
          SystraceSection s("JSCExecutor::callFunction");
          auto result = [&] {
            JSContextLock lock(m_context);
            try {
              if (!m_callFunctionReturnResultAndFlushedQueueJS) {
                bindBridge();
              }
              return m_callFunctionReturnFlushedQueueJS->callAsFunction({
                Value(m_context, String::createExpectingAscii(m_context, moduleId)),
                Value(m_context, String::createExpectingAscii(m_context, methodId)),
                Value::fromDynamic(m_context, std::move(arguments))
              });
            } catch (...) {
              std::throw_with_nested(
                std::runtime_error("Error calling " + moduleId + "." + methodId));
            }
          }();
          callNativeModules(std::move(result));
        }
    

    이 방법 은 js 단 JavaScriptModule 대응 방법 을 호출 하고 현재 js 단 에서 시작 하 는 NativeModule 비동기 방법 요청 목록 을 얻 은 다음 에 callNativeModules 방법 을 호출 합 니 다. 이 방법 은 JsToNativeBridge 의 callNativeModules 방법 을 호출 하여 자바 단 코드 를 호출 합 니 다.
    void JSCExecutor::invokeCallback(const double callbackId, 
           const folly::dynamic& arguments) {
          SystraceSection s("JSCExecutor::invokeCallback");
          auto result = [&] {
            JSContextLock lock(m_context);
            try {
              if (!m_invokeCallbackAndReturnFlushedQueueJS) {
                bindBridge();
              }
              return m_invokeCallbackAndReturnFlushedQueueJS->callAsFunction({
                Value::makeNumber(m_context, callbackId),
                Value::fromDynamic(m_context, std::move(arguments))
              });
            } catch (...) {
              std::throw_with_nested(
              std::runtime_error(folly::to<:string>("Error invoking callback ", 
                       callbackId)));
            }
          }();
          callNativeModules(std::move(result));
        }
    

    이전 방법 과 마찬가지 로 js 단 대응 방법 을 호출 하여 현재 js 단 에서 시 작 된 NativeModule 비동기 방법 요청 목록 을 얻 고 callNativeModules 방법 을 호출 합 니 다.
    void JSCExecutor::callNativeModules(Value&& value) {
          SystraceSection s("JSCExecutor::callNativeModules");
          CHECK(m_delegate) << "Attempting to use native modules without a delegate";
          try {
            auto calls = value.toJSONString();
            m_delegate->callNativeModules(*this, folly::parseJson(calls), true);
          } catch (...) {
            std::string message = "Error in callNativeModules()";
            try {
              message += ":" + value.toString().str();
            } catch (...) {
              // ignored
            }
            std::throw_with_nested(std::runtime_error(message));
          }
        }
    

    Js ToNative Bridge 의 callNative Modules 방법 을 호출 합 니 다.
    JSValueRef JSCExecutor::nativeCallSyncHook(
              size_t argumentCount,
             const JSValueRef arguments[]) {
          if (argumentCount != 3) {
            throw std::invalid_argument("Got wrong number of args");
          }
    
          unsigned int moduleId = Value(m_context, arguments[0]).asUnsignedInteger();
          unsigned int methodId = Value(m_context, arguments[1]).asUnsignedInteger();
          folly::dynamic args = 
               folly::parseJson(Value(m_context, arguments[2]).toJSONString());
    
          if (!args.isArray()) {
            throw std::invalid_argument(
                  folly::to<:string>(
                   "method parameters should be array, but are ", args.typeName()));
          }
    
          MethodCallResult result = m_delegate->callSerializableNativeHook(
                                                                      *this,
                                                                     moduleId,
                                                                     methodId,
                                                                     std::move(args));
          if (!result.hasValue()) {
            return Value::makeUndefined(m_context);
          }
          return Value::fromDynamic(m_context, result.value());
        }
    

    이 방법 은 js 단 NativeModules. js 에서 호출 되 며, js 단 에서 NativeModule 의 동기 화 방법 을 호출 할 때 이 방법 으로 호출 됩 니 다.
    JsToNativeBridge -> callSerializableNativeHook 방법 을 호출 한 다음 ModuleRegistry -> callSerializableNativeHook 방법 을 호출 하여 자바 모듈 러 Wrapper -> callSerializableNativeHook 방법 을 계속 호출 합 니 다. 이 방법 은 자바 엔 드 NativeModule 대응 방법 을 직접 호출 하고 결과 값 을 되 돌려 줍 니 다.(자바 모듈 러 Wrapper. java 와 자바 MethodWrapper. java 의 invoke 방법 을 호출 하지 않 았 습 니 다. 리 셋 방법 으로 js 단 에 결과 값 을 전달 하기 때문에 결과 값 을 직접 되 돌 릴 수 없습니다.)
    자바 와 c++ 코드 의 상호 호출, js 와 c + 코드 의 상호 호출 에 대해 말씀 드 리 겠 습 니 다. 자바 가 jni 를 통 해 c++ 엔 드 코드 와 의 상호 호출 을 실현 할 수 있다 는 것 을 잘 알 고 있 습 니 다. 그러면 js 는 어떻게 이 루어 집 니까? 전역 변수 global, c + global 을 통 해 js 엔 드 의 인 스 턴 스 와 방법 을 얻 을 수 있 고 global 전역 변수 에 c + 로 컬 방법 을 등록 할 수 있 습 니 다. 그리고 js 엔 드 는global 에서 c++ 방법 을 호출 합 니 다. 예 를 들 어 installNativeHook ("nativeCallSyncHook") 은 전역 변수 에 nativeCallSyncHook 이라는 로 컬 방법 을 등록 하 는 것 입 니 다.
    총결산
    자바 엔 드 -> js 엔 드:
    CatalystInstanceImpl. cpp 부터 Instance. cpp 대응 방법 을 호출 하고 NativeToJs Bridge. cpp 대응 방법 을 호출 한 다음 에 JScExecutor. cpp 대응 방법 을 호출 합 니 다. 최종 적 으로 JScExecutor 에서 js 단 방법 을 호출 합 니 다 (JScExecutor 가 js 대상 방법 을 가지 고 있 기 때 문 입 니 다)
    js 단 -> 자바 단:
  • 비동기 방법의 호출 프로 세 스: JSCExecutor. cpp 의 callNativeModules 방법 을 통 해 JSToNativeBridge (NativeToJs Bridge. cpp 에서) 의 callNativeModules 방법 을 되 돌려 주 고 ModuleRegistry. cpp 의 callNativeMethod 방법 을 되 돌려 주 고 자바 Module Wrapper. cpp 의 invoke 방법 을 호출 한 다음 자바 Module Wrapper. 자바 의 invoke 방법 으로 호출 합 니 다.
  • 동기 화 방법의 호출 절차: JScExecutor. cpp 의 nativeCallSyncHook 방법 을 통 해 JSToNativeBridge (NativeToJSBridge. cpp 에서) 를 되 돌려 줍 니 다.의 callSerializableNativeHook 방법, 모듈 레지스터 리. cpp 의 callSerializableNativeHook 방법, 자바 모듈 러 Wrapper. cpp 의 callSerializableNativeHook 방법 을 리 셋 한 다음 자바 엔 드 NativeModule 에 대응 하 는 방법 을 직접 호출 하여 결과 값 을 되 돌려 줍 니 다.
  • 다음 미리보기
    이 장 은 c++ 단 통신 프로 세 스 의 소스 코드 를 분 석 했 습 니 다. 그러면 다음 장 은 js 단의 통신 체제 의 소스 코드 를 분석 합 니 다.

    좋은 웹페이지 즐겨찾기