React Native 源码分析(五)—— Fabric创建View的过程

这篇文章详细分析一下,在React Native 新架构下,Fabric是如何创建View的,从React层发送把View信息到原生端开始分析。说明一点,React 层fiber的创建更新过程,不属于Fabric。其中Yoga的绘制过程不会太详细,只会给出大概流程,像布局缓存这些。文章的重点是帮你理解Fabric的整体流程。代码分析以断点截图方式体现,可以更方便查看运行的过程

1、React Native 源码分析(一)—— 启动流程

2、React Native 源码分析(二)—— Native Modules桥通信机制

3、React Native 源码分析(三)—— Native View创建流程(桥通信)

4、React Native 源码分析(四)—— TurboModules JSI通信机制

5、React Native 源码分析(五)—— Fabric创建View的过程

本篇文章以显示一个{xuexuan}为例,来分析源码

一、Fabric 的初始化

在React Native 源码分析(一)—— 启动流程一文中,知道了RN启动的过程,会依次创建 ReactNativeHost -> ReactInstanceManagerBuilder -> ReactInstanceManager -> ReactContext

图1.1 createReactInstanceManager

createReactInstanceManager

在使用ReactNative时,会在Application中重写ReactNativeHost,会继承DefaultReactNativeHost(React Native 提供的),你可以重载getJSIModulePackage(),但通常使用默认的

代码段1:
//路径packages/react-native/ReactAndroid/src/main/java/com/facebook/react/defaults/DefaultReactNativeHost.kt
  override fun getJSIModulePackage(): JSIModulePackage? =
      if (isNewArchEnabled) {
        //见下方的代码
        DefaultJSIModulePackage(this)
      } else {
        null
      }

图1.1 .setJSIModulesPackage(getJSIModulePackage()) 最终 会把对象 DefaultJSIModulePackage传递到ReactInstanceManager对象的mJSIModulePackage(在ReactInstanceManager的构造函数中),接下来会在创建ReactContext的函数createReactContext中使用到mJSIModulePackage

图1.2 createReactContext的代码中:

在这里插入图片描述

代码2: CatalystInstance类

   //1397行调用该函数
  @Override
  public void addJSIModules(List jsiModules) {
    // 该方法会把jsiModules包装成JSIModuleHolder,然后进行添加
    mJSIModuleRegistry.registerModules(jsiModules);
  }

  //1402行调用该函数
  @Override
  public JSIModule getJSIModule(JSIModuleType moduleType) {
    // 获取时,通过JSIModuleHolder获取,其中会进行一些额外操作
    //    mModule = mSpec.getJSIModuleProvider().get();
    //    mModule.initialize();
    return mJSIModuleRegistry.getModule(moduleType);
  }

1397行 addJSIModules 的参数 DefaultJSIModulePackage#JSIModulePackage的返回值,见下段代码3

1402行 getJSIModule ,因为添加时mJSIModuleRegistry.registerModules会把入参包装成JSIModuleHolder ,所以获取时也是通过该类(获取的同时初始化FabricUIManager )

1.2 创建FabricJSIModuleProvider、创建所有ViewManager

继续接着 ReactInstanceManager 的 1402行 getJSIModule -> mSpec.getJSIModuleProvider().get() ,其中getJSIModuleProvider() 就是调用到 图1.3的JSIModuleForFabric#getJSIModuleProvider()

  1. reactNativeHost.reactInstanceManager.getOrCreateViewManagers(reactApplicationContext)创建出所有ViewManager(在自定义的Application中初始化的),(通过查看下图的调用堆栈,本例是初始化了MainReactPackage中ReactDrawerLayoutManager)
  2. 创建了FabricJSIModuleProvider,mSpec.getJSIModuleProvider().get() 其中的get就是调用FabricJSIModuleProvider方法里的get

图1.3

在这里插入图片描述

1.3 创建FabricUIManager 与C++关联

接着分析mSpec.getJSIModuleProvider().get()中的get(),也就是调用了FabricJSIModuleProvider#get,如下图

图1.4

在这里插入图片描述

1、创建 FabricUIManager,负责View的创建、布局测量等,会把这些任务加入到ChoreographerCompat.FrameCallback

2、与Binging.java (packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/Binding.java)关联起来,它的很多方法是调用到Binging.cpp (packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp)

1.4 C++层初始化Binging.cpp、Scheduler.cpp

接着进入图1.4的46行,到Binging.java#register-> Binding.cpp#installFabricUIManager , 后面的流程就是到c++中初始化

代码段3:
代码路径:packages/react-native/ReactAndroid/src/main/jni/react/fabric/Binding.cpp

void Binding::installFabricUIManager(
    jni::alias_ref runtimeExecutorHolder,
    jni::alias_ref runtimeSchedulerHolder,
    jni::alias_ref javaUIManager,
    EventBeatManager *eventBeatManager,
    ComponentFactory *componentsRegistry,
    jni::alias_ref reactNativeConfig) {
  ...
  std::shared_ptr config =
      std::make_shared(reactNativeConfig);

  enableFabricLogs_ =
      config->getBool("react_fabric:enabled_android_fabric_logs");
  ...

  // globalJavaUiManager 就是java层的 FabricUIManager  
  auto globalJavaUiManager = make_global(javaUIManager);
  mountingManager_ =
      std::make_shared(config, globalJavaUiManager);

  // 这个contextContainer很重要,它会传入具体某个View的
  ContextContainer::Shared contextContainer =
      std::make_shared();

  ...省略若干代码...

  contextContainer->insert("ReactNativeConfig", config);
  //在View的测量过程,需要通过"FabricUIManager" 获取到globalJavaUiManager(java层的 FabricUIManager )
  contextContainer->insert("FabricUIManager", globalJavaUiManager);
  ...省略若干代码...
 
  auto toolbox = SchedulerToolbox{};
  toolbox.contextContainer = contextContainer;
  // 后续会通过该方法 收集component(也就是View) 的信息 
  // componentsRegistry 就是java层的ComponentFactory,路径:packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/ComponentFactory.java
  // 对应c++的类是DefaultComponentsRegistry.cpp 路径: packages/react-native/ReactAndroid/src/main/jni/react/newarchdefaults/DefaultComponentsRegistry.cpp
  toolbox.componentRegistryFactory = componentsRegistry->buildRegistryFunction;

  // TODO: (T130208323) runtimeExecutor should execute lambdas after
  // main bundle eval, and bindingsInstallExecutor should execute before.
  toolbox.bridgelessBindingsExecutor = std::nullopt;
  toolbox.runtimeExecutor = runtimeExecutor;

  toolbox.synchronousEventBeatFactory = synchronousBeatFactory;
  toolbox.asynchronousEventBeatFactory = asynchronousBeatFactory;

  ...省略若干代码...
  //创建Scheduler
  scheduler_ =
      std::make_shared(toolbox, animationDriver_.get(), this);
}

在调用Binging.cpp#installFabricUIManager时,参数ComponentFactory *componentsRegistry,需要创建ComponentFactory对象,就会调用ComponentFactory#initHybrid ->DefaultComponentsRegistry::initHybrid,此时就设置好了buildRegistryFunction函数。用于收集所有View对应的对象,后续创建View时,就能方便找到对象

接着来看创建Scheduler

代码段4:
代码路径:packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp

Scheduler::Scheduler(
    SchedulerToolbox const &schedulerToolbox,
    UIManagerAnimationDelegate *animationDelegate,
    SchedulerDelegate *delegate) {
  runtimeExecutor_ = schedulerToolbox.runtimeExecutor;
  contextContainer_ = schedulerToolbox.contextContainer;

  ...省略若干代码...

  auto uiManager = std::make_shared(
      runtimeExecutor_, schedulerToolbox.backgroundExecutor, contextContainer_);
      
  ...省略若干代码...
  
  //先获取所有组件的Provider,再通过每个Provider创建对应的ComponentDescriptor
  componentDescriptorRegistry_ = schedulerToolbox.componentRegistryFactory(
      eventDispatcher, contextContainer_);
  // 把Scheduler对象设置到 UIManager.cpp中的delegate_变量中
  // UIManager.cpp 路径packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp
  uiManager->setDelegate(this);
  uiManager->setComponentDescriptorRegistry(componentDescriptorRegistry_);

  auto bindingsExecutor =
      schedulerToolbox.bridgelessBindingsExecutor.has_value()
      ? schedulerToolbox.bridgelessBindingsExecutor.value()
      : runtimeExecutor_;
  bindingsExecutor([uiManager](jsi::Runtime &runtime) {
    // 该方法是Fabric 中JSI的关键代码:
    //1、使用uiManager创建UIManagerBinding,
    //2、通过JSI把后者添加到JS VM中,变量名称为nativeFabricUIManager的,这样在JS中就可以通过nativeFabricUIManager变量访问UIManagerBinding中的方法
    UIManagerBinding::createAndInstallIfNeeded(runtime, uiManager);
  });

  auto componentDescriptorRegistryKey =
      "ComponentDescriptorRegistry_DO_NOT_USE_PRETTY_PLEASE";
  contextContainer_->erase(componentDescriptorRegistryKey);
  contextContainer_->insert(
      componentDescriptorRegistryKey,
      std::weak_ptr(
          componentDescriptorRegistry_));

  delegate_ = delegate;
  commitHooks_ = schedulerToolbox.commitHooks;
  uiManager_ = uiManager;

  for (auto const &commitHook : commitHooks_) {
    uiManager->registerCommitHook(*commitHook);
  }
   ...省略若干代码...
  uiManager_->setAnimationDelegate(animationDelegate);
}

至此初始化就算完成了,下面还要分析一下上面的这行代码schedulerToolbox.componentRegistryFactory( eventDispatcher, contextContainer_); 它用来收集组件的相关信息

1.5 收集View的信息

接着来分析一下componentRegistryFactory,该函数是在DefaultComponentsRegistry#initHybrid中设置的buildRegistryFunction

图1.5在这里插入图片描述

断点所在行,

  1. DefaultComponentsRegistry::sharedProviderRegistry() 用于收集所有组件的Provider
  2. ->createComponentDescriptorRegistry({eventDispatcher, contextContainer}); 用于收集所有组件的ComponentDescriptor

下面依次来看下这两点对应的代码:

第一点对应的代码:

使用ComponentDescriptor作为类型,创建对应的concreteComponentDescriptorProvider对象

图1.6

在这里插入图片描述

第二点对应的代码:

图1.7

在这里插入图片描述

33行,就是调用组件的 ComponentDescriptor构造函数(例如:AndroidDrawerLayoutComponentDescriptor、ParagraphComponentDescriptor),

在Fabric的机制中,自定义的组件,使用CodeGen会生成相关代码,如下图,这里的componentDescriptorProvider.constructor 对应的就是这些ComponentDescriptor(下一节介绍)

在这里插入图片描述

介绍的是几个关键点,没有把每个调用流程的代码都贴出来,你可以实际跟一下代码,

二、Fabric机制中的ComponentDescriptor

这里以using AndroidDrawerLayoutComponentDescriptor = ConcreteComponentDescriptor; 来分析,这个也是通过codeGen生成的,更贴近开发中自定义View组件的生成代码,

像是ReactNative中自带的Text,已经把这些类写到了目录packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text中,与codeGen生成的代码,是一一对应的

2.1 ConcreteComponentDescriptor

该类是这样使用的

using AndroidDrawerLayoutComponentDescriptor = ConcreteComponentDescriptor;

根据泛型AndroidDrawerLayoutShadowNode,来创建ConcreteComponentDescriptor,通过它来创建AndroidDrawerLayoutShadowNode的对象 createShadowNode,getComponentName、appendChild 等等,

也就是说外界想要对 ShadowNode 操作,都需要通过ConcreteComponentDescriptor

2.2 AndroidDrawerLayoutShadowNode

以AndroidDrawerLayoutShadowNode 来分析一下,其他组件都类似,其他它是ConcreteViewShadowNode类,泛型的类,都会在CodeGen自动生成

在这里插入图片描述

ConcreteViewShadowNode 继承 ConcreteShadowNode 继承 YogaLayoutableShadowNode,在Fabric也是有这三颗树,和旧版的一样。

下面来看一下ConcreteShadowNode、YogaLayoutableShadowNode,好好体会一下,传参和类变量

路径:packages/react-native/ReactCommon/react/renderer/components/view/ConcreteViewShadowNode.h
template 
class ConcreteViewShadowNode : public ConcreteShadowNode 

路径:packages/react-native/ReactCommon/react/renderer/core/ConcreteShadowNode.h
template 
class ConcreteShadowNode : public BaseShadowNodeT {

 public:
  //YogaLayoutableShadowNode 类型
  using BaseShadowNodeT::BaseShadowNodeT;
  //AndroidDrawerLayoutProps类型
  using ConcreteProps = PropsT;
  using SharedConcreteProps = std::shared_ptr;
  using UnsharedConcreteProps = std::shared_ptr;
  //AndroidDrawerLayoutEventEmitter类型
  using ConcreteEventEmitter = EventEmitterT;
  using SharedConcreteEventEmitter = std::shared_ptr;
  //AndroidDrawerLayoutState类型
  using SharedConcreteShadowNode = std::shared_ptr;
  using ConcreteState = ConcreteState;
  using ConcreteStateData = StateDataT;

三、Fabric 机制下View的创建

3.1 js端调用入口

在react中,对所有组件计算后,最终会生成fiber树,然后通过global.nativeFabricUIManager调用到C++层,也就是在JS VM,这个变量的设置是在代码段4中,UIManagerBinding::createAndInstallIfNeeded(runtime, uiManager);

所以也就是调用到UIManagerBinding.cpp,

下图是React的组件绘制类,最终fiber树的每个节点和关键时机,都会通过这些函数调用到原生端,来创建View

在这里插入图片描述

3.2 创建View

在js端调用createNode = _nativeFabricUIManage.createNode 就会调用到下面的UIManagerBinding.cpp的代码中,如下。(UIManagerBinding 继承jsi::HostObject,所以在js中直接通过.createNode形式调用,会调用到UIManagerBinding.cpp中的get函数,上篇文章有详细分析)

3.2.1 创建ShadowNode

代码路径:packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp
jsi::Value UIManagerBinding::get(
    jsi::Runtime &runtime,
    jsi::PropNameID const &name) {
  if (methodName == "createNode") {
    //把createNode 通过jsi加入到js vm中,它是一个函数,具体函数是下面的lambda
    return jsi::Function::createFromHostFunction(
        runtime,
        name,
        5,
        [uiManager](
            jsi::Runtime &runtime,
            jsi::Value const & /*thisValue*/,
            jsi::Value const *arguments,
            size_t /*count*/) noexcept -> jsi::Value {
          auto eventTarget =
              eventTargetFromValue(runtime, arguments[4], arguments[0]);
          if (!eventTarget) {
            react_native_assert(false);
            return jsi::Value::undefined();
          }
          return valueFromShadowNode(
              runtime,
              //packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp
              //通过UIManager.cpp来创建Node,这个node是Yoga引擎下的ShadowNode
              uiManager->createNode(
                  tagFromValue(arguments[0]),
                  stringFromValue(runtime, arguments[1]),
                  surfaceIdFromValue(runtime, arguments[2]),
                  RawProps(runtime, arguments[3]),
                  eventTarget));
        });
  }
}  

下面来看一下 uiManager->createNode的代码:

代码路径:packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp

ShadowNode::Shared UIManager::createNode(
    Tag tag,
    std::string const &name,
    SurfaceId surfaceId,
    const RawProps &rawProps,
    SharedEventTarget eventTarget) const {
  SystraceSection s("UIManager::createNode");
  
  // 获取到对应组件的componentDescriptor,在第二章中有介绍,外部操作和访问 组件都需要通过它的componentDescriptor
  auto &componentDescriptor = componentDescriptorRegistry_->at(name);
  auto fallbackDescriptor =
      componentDescriptorRegistry_->getFallbackComponentDescriptor();

  PropsParserContext propsParserContext{surfaceId, *contextContainer_.get()};

  auto const fragment = ShadowNodeFamilyFragment{tag, surfaceId, nullptr};
  auto family =
      componentDescriptor.createFamily(fragment, std::move(eventTarget));
  auto const props =
      componentDescriptor.cloneProps(propsParserContext, nullptr, rawProps);
  auto const state =
      componentDescriptor.createInitialState(ShadowNodeFragment{props}, family);
  //创建对应的ShadowNode,该对象的间接继承了YogaLayoutableShadowNode,所以后续会通过Yoga来进行测量
  auto shadowNode = componentDescriptor.createShadowNode(
      ShadowNodeFragment{
          /* .props = */
          ...省略若干代码...
           props,
          /* .children = */ ShadowNodeFragment::childrenPlaceholder(),
          /* .state = */ state,
      },
      family);

  if (delegate_ != nullptr) {
    // 这个delegate_ 就是Scheduler,在初始化Scheduler时进行设置的
    delegate_->uiManagerDidCreateShadowNode(*shadowNode);
  }
  ...省略若干代码...

  return shadowNode;
}

注意这里有一点非常重要,componentDescriptorRegistry_->at(name)根据名称获取componentDescriptor,在at中进行了一些转换,例如

代码路径:packages/react-native/ReactCommon/react/renderer/componentregistry/componentNameByReactViewName.cpp
std::string componentNameByReactViewName(std::string viewName) {

  std::string rctPrefix("RCT");
  if (std::mismatch(rctPrefix.begin(), rctPrefix.end(), viewName.begin())
          .first == rctPrefix.end()) {
    // If `viewName` has "RCT" prefix, remove it.
    viewName.erase(0, rctPrefix.length());
  }
  // Fabric uses slightly new names for Text components because of differences
  // in semantic.
  //这就是为什么React用的是RCTText,在原生端用的确实Paragraph的componentDescriptor
  if (viewName == "Text") {
    return "Paragraph";
  }

  return viewName;
}

Scheduler::uiManagerDidCreateShadowNode

-> Binding::schedulerDidRequestPreliminaryViewAllocation

-> Binding::preallocateView

把ShadowNode的componentName、props、tags等信息,提取到ShadowView对象中

-> FabricMountingManager::preallocateShadowView 根据ShadowView对象的信息,若缓存没有找到该View信息,则调用到java创建 View

-> FabricUIManager.java # preallocateView 把组件的相关信息收集到PreAllocateViewMountItem对象中,把该对象添加到 任务分配器 mMountItemDispatcher,这样只需要告诉mMountItemDispatcher 去执行任务,它就会按照这些任务的类型,去先后执行

路径:
  private void preallocateView(
      int rootTag,
      int reactTag,
      final String componentName,
      @Nullable Object props,
      @Nullable Object stateWrapper,
      @Nullable Object eventEmitterWrapper,
      boolean isLayoutable) {
    //添加任务到mMountItemDispatcher对象的mPreMountItems变量中,
    // 这些任务会在Android 垂直同步信号到达时,设置的回调中被执行,就是下一节分析的流程
    mMountItemDispatcher.addPreAllocateMountItem(
        new PreAllocateViewMountItem(
            rootTag,
            reactTag,
            getFabricComponentName(componentName),
            props,
            (StateWrapper) stateWrapper,
            (EventEmitterWrapper) eventEmitterWrapper,
            isLayoutable));
  }

3.2.2 ChoreographerCompat.FrameCallback 的回调中触发任务

任务的触发,会在ChoreographerCompat.FrameCallback 的回调中触发:

代码路径:packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java

    public void doFrameGuarded(long frameTimeNanos) {
      ...省略若干代码...

      try {
        //调用 mPreMountItems 变量中,存储的任务,这些任务是创建View,所以需要先执行。
        // 此时的View还没有被测量布局,所以还不会被看到
        mMountItemDispatcher.dispatchPreMountItems(frameTimeNanos);
        // 
        mMountItemDispatcher.tryDispatchMountItems();
      } catch (Exception ex) {
        ...省略若干代码...
      } finally {
        ReactChoreographer.getInstance()
            .postFrameCallback(
                ReactChoreographer.CallbackType.DISPATCH_UI, mDispatchUIFrameCallback);
      }
    }

下面来分析一下dispatchPreMountItems,tryDispatchMountItems的逻辑会在commit阶段分析到,那时会直接调用到该函数,而不用等ChoreographerCompat.FrameCallback 的回调中触发

代码路径:packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.java

  public void dispatchPreMountItems(long frameTimeNanos) {
     ...省略若干代码...

    // dispatchPreMountItems cannot be reentrant, but we want to prevent dispatchMountItems from
    // reentering during dispatchPreMountItems
    mInDispatch = true;

    try {
      while (true) {
        ...省略若干代码...
        // 获取到 mMountItemDispatcher.addPreAllocateMountItem 添加的任务对象
        PreAllocateViewMountItem preMountItemToDispatch = mPreMountItems.poll();
        // If list is empty, `poll` will return null, or var will never be set
        if (preMountItemToDispatch == null) {
          break;
        }
         ...省略若干代码...
         // 执行该对象里的任务,也就是PreAllocateViewMountItem里的任务,下一节分析
        executeOrEnqueue(preMountItemToDispatch);
      }
    } finally {
      mInDispatch = false;
    }
     ...省略若干代码...
  }

3.2.3 创建原生View

接着到PreAllocateViewMountItem中,看看任务具体是如何执行的

  @Override
  public void execute(@NonNull MountingManager mountingManager) {
    SurfaceMountingManager surfaceMountingManager = mountingManager.getSurfaceManager(mSurfaceId);
    
    surfaceMountingManager.preallocateView(
        mComponent, mReactTag, mProps, mStateWrapper, mEventEmitterWrapper, mIsLayoutable);
  }

调用过程 surfaceMountingManager.preallocateView -> createViewUnsafe , 后者代码如下:

代码路径:packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java

  @UiThread
  public void createViewUnsafe(
      @NonNull String componentName,
      int reactTag,
      @Nullable Object props,
      @Nullable StateWrapper stateWrapper,
      @Nullable EventEmitterWrapper eventEmitterWrapper,
      boolean isLayoutable) {
    View view = null;
    ReactViewManagerWrapper viewManager = null;
    Object propMap;
    if (props instanceof ReadableMap) {
      // 对ReadableMap类型的props封装成 ReactStylesDiffMap,目的是为了方便获取数据
      propMap = new ReactStylesDiffMap((ReadableMap) props);
    } else {
      propMap = props;
    }

    if (isLayoutable) {
      // 这里是获取到DefaultViewManager,这个类是个中转类,最终的调用是转交给对应的ViewManager
      viewManager =
          props instanceof ReadableMapBuffer
              ? ReactMapBufferViewManager.INSTANCE
              //根据组件名称componentName,获取到对应的ViewManager,
              //如果开发过自定义View就知道,每个View都需要提供ViewManager,通过它来创建具体的View
              : new ReactViewManagerWrapper.DefaultViewManager(mViewManagerRegistry.get(componentName));
      // View Managers are responsible for dealing with initial state and props.
      view =
          viewManager.createView(
              reactTag, mThemedReactContext, propMap, stateWrapper, mJSResponderHandler);
    }

    ViewState viewState = new ViewState(reactTag, view, viewManager);
    viewState.mCurrentProps = propMap;
    viewState.mStateWrapper = stateWrapper;
    viewState.mEventEmitter = eventEmitterWrapper;

    mTagToViewState.put(reactTag, viewState);
  }

到此,已经创建出组件对应的原生View,这也是 _nativeFabricUIManage.createNode函数所触发的原生动作,熟悉React的fiber绘制流程的朋友,应该知道,最终会有complete阶段,对应会调用_nativeFabricUIManage.completeRoot,在这其中会使用Yoga引擎对View进行测量,最终显示出来

3.3 commit

该阶段的主要调用过程,

UIManager::completeSurface 
-> ShadowTree::commit 
-> ShadowTree::tryCommit
-> RootShadowNode::layoutIfNeeded  //从根节点开始计算
-> YogaLayoutableShadowNode::layoutTree  
-> Yoga.cpp # YGNodeCalculateLayoutWithContext 
-> YGLayoutNodeInternal 
-> YGNodelayoutImpl //This is the main routine that implements a subset of the flexbox layout algorithm 
-> YGNodeComputeFlexBasisForChildren //遍历测量每个child
-> YGNodeComputeFlexBasisForChild

-> YGNodeWithMeasureFuncSetMeasuredDimensions //调用到某个YGNode节点的measure函数

调用到UIManager::completeSurface 函数中,遍历shadowTreeRegistry,对每一个shadowTree进行commit,shadowTree是在startSurface中添加到shadowTreeRegistry_ 的

在这里插入图片描述

3.3.1 yoga布局计算

既然是对 shadowTree进行commit ,那肯定会计算这棵树每个Node节点的布局,首先从根节点开始。在

ShadowTree::commit -> ShadowTree::tryCommit , 在tryCommit中获取到该shadowTree的根节点,开始从他进行layoutTree

在这里插入图片描述

下面就进入到了Yoga的绘制流程中,比较复杂,看下调用堆栈,下面我们直接来到具体的Text组件的绘制函数中,看看Yogo是如何调用它的measure函数

在这里插入图片描述

直接来到调用堆栈的最末端,来看下具体的Text的measure

在这里插入图片描述

上图中paragraphLayoutManager,是在初始化ParagraphComponentDescriptor时进行赋值的,还记得创建ComponentDescriptor的时机吗?(图1.7 的33行),会给赋值TextLayoutManager 到变量textLayoutManager_

class ParagraphComponentDescriptor final
    : public ConcreteComponentDescriptor {
 public:
  ParagraphComponentDescriptor(ComponentDescriptorParameters const &parameters)
      : ConcreteComponentDescriptor(parameters) {
    // Every single `ParagraphShadowNode` will have a reference to
    // a shared `TextLayoutManager`.
    textLayoutManager_ = std::make_shared(contextContainer_);
  }

 protected:
  //该函数会在3.2.1 创建ShadowNode时,componentDescriptor.createShadowNode中 调用该函数
  void adopt(ShadowNode::Unshared const &shadowNode) const override {
    ConcreteComponentDescriptor::adopt(shadowNode);

    auto paragraphShadowNode =
        std::static_pointer_cast(shadowNode);

    // `ParagraphShadowNode` uses `TextLayoutManager` to measure text content
    // and communicate text rendering metrics to mounting layer.
    paragraphShadowNode->setTextLayoutManager(textLayoutManager_);
  }

 private:
  std::shared_ptr textLayoutManager_;
};

在这里插入图片描述

textLayoutManager_->measure (也就是TextLayoutManager::measure)

->TextLayoutManager::doMeasureMapBuffer

->measureAndroidComponentMapBuffer 代码如下,可以看到是调用了FabricUIManager,最后可以调用到自定义ViewManager的measure函数,这样我们就有机会控制View的测量

在这里插入图片描述

通过下图的调用堆栈,可以看到,先调用到FabricUIManager,再交由MountingManager,根据ComponentName找到具体的ViewManager,调用它的measure函数

在这里插入图片描述

接着继续tryCommit 继续往下执行,来到 mount(std::move(newRevision), commitOptions.mountSynchronously);

3.3.2 mount

也是直接来到packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp 的executeMount函数,这里是关键,中间几次调用基本都是单纯的转向调用,没有太多逻辑

在这里插入图片描述

void FabricMountingManager::executeMount(
    MountingCoordinator::Shared mountingCoordinator) {
  ...省略若干代码...
  //static auto UIManagerJavaDescriptor =
      "com/facebook/react/fabric/FabricUIManager";
  // 获取到java层 FabricUIManager的scheduleMountItem函数,最终挂载,属性更新,还是要用到java层的View
  static auto scheduleMountItem = jni::findClassStatic(UIManagerJavaDescriptor)
                                      ->getMethod("scheduleMountItem");


  ...省略若干代码...
 if (mountItemType == CppMountItem::Type::Create) {
      ...省略若干代码...
      
      env->SetIntArrayRegion(intBufferArray, intBufferPosition, 2, temp);
      intBufferPosition += 2;

      // 传递参数的组织,统统在一个数组中,不同变量值 写在数组的不同位置
      (*objBufferArray)[objBufferPosition++] = componentName.get();
      (*objBufferArray)[objBufferPosition++] = props.get();
      (*objBufferArray)[objBufferPosition++] =
          javaStateWrapper != nullptr ? javaStateWrapper.get() : nullptr;
      (*objBufferArray)[objBufferPosition++] = javaEventEmitter.get();
    } 
  }
  ...省略若干代码...
  // If there are no items, we pass a nullptr instead of passing the object
  // through the JNI
  auto batch = createMountItemsIntBufferBatchContainer(
      javaUIManager_,
      surfaceId,
      batchMountItemIntsSize == 0 ? nullptr : intBufferArray,
      batchMountItemObjectsSize == 0 ? nullptr : objBufferArray.get(),
      revisionNumber);

 
  // 最终调用到FabricUIManager的scheduleMountItem
  scheduleMountItem(
      javaUIManager_,
      batch.get(),
      telemetry.getRevisionNumber(),
      telemetryTimePointToMilliseconds(telemetry.getCommitStartTime()),
      telemetryTimePointToMilliseconds(telemetry.getDiffStartTime()),
      telemetryTimePointToMilliseconds(telemetry.getDiffEndTime()),
      telemetryTimePointToMilliseconds(telemetry.getLayoutStartTime()),
      telemetryTimePointToMilliseconds(telemetry.getLayoutEndTime()),
      telemetryTimePointToMilliseconds(finishTransactionStartTime),
      telemetryTimePointToMilliseconds(finishTransactionEndTime));

}
private void scheduleMountItem(
      @Nullable final MountItem mountItem,
      int commitNumber,
      long commitStartTime,
      long diffStartTime,
      long diffEndTime,
      long layoutStartTime,
      long layoutEndTime,
      long finishTransactionStartTime,
      long finishTransactionEndTime) {
   ...省略若干代码...
  
    if (shouldSchedule) {
      // 把该任务加入到mMountItemDispatcher,还记得之前的mPreMountItems,还那个类似,也会在Choreographer回调中,触发该任务
      mMountItemDispatcher.addMountItem(mountItem);
      
      Runnable runnable =
          new Runnable() {
            @Override
            public void run() {
              //在Choreographer回调中,也调用了它
              mMountItemDispatcher.tryDispatchMountItems();
            }
          };
      //为什么这里需要手动执行,而不是等  Choreographer回调呢?
      //因为此时React树、ShadowNode树已经准备完了,可以 早一些运行这些任务,下一个Choreographer回调的任务量就会小一些,从而提升页面流畅度
      if (UiThreadUtil.isOnUiThread()) {
        runnable.run();
      } else {
        // The Choreographer will dispatch any mount items,
        // but it only gets called at the /beginning/ of the
        // frame - it has no idea if, or when, there is actually work scheduled. That means if we
        // have a big chunk of work
        // scheduled but the scheduling happens 1ms after the
        // start of a UI frame, we'll miss out on 15ms of time
        // to perform the work (assuming a 16ms frame).
        // The DispatchUIFrameCallback still has value because of
        // the PreMountItems that we need to process at a lower
        // priority.
        if (ReactFeatureFlags.enableEarlyScheduledMountItemExecution) {
          UiThreadUtil.runOnUiThread(runnable);
        }
      }
    }
}

接着mMountItemDispatcher.tryDispatchMountItems() 往下分析,来到dispatchMountItems

private boolean dispatchMountItems() {
    ...省略若干代码...
    List viewCommandMountItemsToDispatch =
        getAndResetViewCommandMountItems();
    List mountItemsToDispatch = getAndResetMountItems();

    ...省略若干代码...

    mItemDispatchListener.willMountItems();

    ...省略若干代码...
    // If there are MountItems to dispatch, we make sure all the "pre mount items" are executed
    // first
    Collection preMountItemsToDispatch = getAndResetPreMountItems();
    //要确保preMountItems一定被执行完,因为它涉及到View的创建,现有了View对象,才能对它更新
    if (preMountItemsToDispatch != null) {
      for (PreAllocateViewMountItem preMountItem : preMountItemsToDispatch) {
        executeOrEnqueue(preMountItem);
      }
    }
    // 对 View的属性进行更新
    if (mountItemsToDispatch != null) {
      for (MountItem mountItem : mountItemsToDispatch) {
        try {
          executeOrEnqueue(mountItem);
        } catch (Throwable e) {
           ...省略若干代码...
        }
      }
       ...省略若干代码...
    }
   ...省略若干代码...
    mItemDispatchListener.didMountItems();

    ...省略若干代码...
    return true;
  }

主要流程的代码很简单,省略了很多细节代码,mountItemsToDispatch 中的每个MountItem,可能操作类型都不一样,看下本例的调用堆栈

在这里插入图片描述

MountItem 是一个接口,有几种不同的类,实现了它,会在具体的实现类中,去从数组中解析参数。最终调用到setText的方法,来设置mText

至此,就分析完了,与主流程关系不大的函数调用,就省略了,把握住大流程,其他的一些小细节,可以实际调试跟一下代码。

本文来自网络,不代表协通编程立场,如若转载,请注明出处:https://www.net2asp.com/2672253635.html