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

在使用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()
- reactNativeHost.reactInstanceManager.getOrCreateViewManagers(reactApplicationContext)创建出所有ViewManager(在自定义的Application中初始化的),(通过查看下图的调用堆栈,本例是初始化了MainReactPackage中ReactDrawerLayoutManager)
- 创建了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
断点所在行,
- DefaultComponentsRegistry::sharedProviderRegistry() 用于收集所有组件的Provider
- ->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 ¶meters)
: 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://net2asp.com/2672253635.html
