React Native之Android端Fabric 架构源码分析
前言
Fabric 是 React Native 新架构的 UI 渲染系统,现在我们就来深入分析其源码。
本文基于React Native 0.83版本源码进行分析。
初始化
在《React Native新架构之Android端初始化源码分析》一文已经提过Fabric的初始化部分,现在回顾一下:
kotlin
// ReactInstance.kt
val eventBeatManager = EventBeatManager()
fabricUIManager =
FabricUIManager(context, ViewManagerRegistry(viewManagerResolver), eventBeatManager)
// 在 Fabric 初始化之前需要完成的其他初始化操作。
DisplayMetricsHolder.initDisplayMetricsIfNotInitialized(context)
val binding = FabricUIManagerBinding()
binding.register(
getBufferedRuntimeExecutor(),
getRuntimeScheduler(),
fabricUIManager,
eventBeatManager,
componentFactory,
)
// 初始化 FabricUIManager
fabricUIManager.initialize()
这里EventBeatManager 是 一个基于观察者模式的 Fabric 架构的 "节拍控制器",它利用 Android 原生的帧回调机制,协调并驱动 C++ 层的事件向 JS 层高效、有序地流动。
现在重点看一FabricUIManager的构造方法做了什么react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java:
java
public FabricUIManager(
ReactApplicationContext reactContext,
ViewManagerRegistry viewManagerRegistry,
BatchEventDispatchedListener batchEventDispatchedListener) {
// 初始化帧回调
mDispatchUIFrameCallback = new DispatchUIFrameCallback(reactContext);
mReactApplicationContext = reactContext;
// 初始化挂载管理器
mMountingManager = new MountingManager(viewManagerRegistry, mMountItemExecutor);
// 初始化挂载指令调度器
mMountItemDispatcher =
new MountItemDispatcher(mMountingManager, new MountItemDispatchListener());
// 初始化事件分发器
mEventDispatcher = new FabricEventDispatcher(reactContext, new FabricEventEmitter(this));
// 持有批处理事件监听器
mBatchEventDispatchedListener = batchEventDispatchedListener;
// 注册生命周期监听
mReactApplicationContext.addLifecycleEventListener(this);
// 注册组件回调
mViewManagerRegistry = viewManagerRegistry;
mReactApplicationContext.registerComponentCallbacks(viewManagerRegistry);
}
这段代码,首先创建一个派生自 Choreographer.FrameCallback的回调,这是 Fabric 渲染的"心脏"。它会注册到 Android 的 Choreographer,在每一帧垂直同步(VSync)信号到来时被调用。它负责驱动 MountItemDispatcher 来执行挂载操作(即实际的 View 更新)。
随后创建了一个挂载管理器MountingManager,它是实际操作 Android View 的管理者(执行 createView, updateProps 等)。接着创建了挂载指令调度器MountItemDispatcher,它负责管理挂载指令(MountItem)的队列,决定它们是在当前线程同步执行还是推入队列等待下一帧执行。当一批指令分发完成后,它会收到回调。这主要用于通知监听器,告诉它们"UI 已经更新了,你们可以进行下一帧动画计算了"。
接下来又创建了事件分发器FabricEventDispatcher,它负责将 Android 原生事件(如 Touch, Scroll)发送给 JavaScript。它的参数FabricEventEmitter是一个实现了 RCTEventEmitter 接口的类,它内部持有 C++ 层的引用(通过 JNI),是 Java 事件通往 C++ Fabric 核心的入口。
以上这写类基本上构成了一套UI系统的核心处理。如果大家需要更深入分析React Native UI系统,那么这些类就是研究的重点。在构造方法的最后,注册了生命周期监听,这是为了让 FabricUIManager 能够感知 Activity/Host 的 onResume, onPause, onDestroy。尤其是在 onHostResume 时恢复 UI 挂载循环,在 onHostPause 时暂停,以节省资源并避免在后台更新 UI。
最后注册组件回调,主要是用于当系统内存不足时,ViewManagerRegistry 可以收到通知并释放缓存。
现在继续分析初始化流程FabricUIManagerBinding的创建,源码react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManagerBinding.kt:
kotlin
@DoNotStrip
@SuppressLint("MissingNativeLoadLibrary")
internal class FabricUIManagerBinding : HybridClassBase() {
init {
initHybrid()
}
private external fun initHybrid()
external fun setPixelDensity(pointScaleFactor: Float)
private external fun installFabricUIManager(
runtimeExecutor: RuntimeExecutor,
runtimeScheduler: RuntimeScheduler,
uiManager: FabricUIManager,
eventBeatManager: EventBeatManager,
componentsRegistry: ComponentFactory,
)
fun register(
runtimeExecutor: RuntimeExecutor,
runtimeScheduler: RuntimeScheduler,
fabricUIManager: FabricUIManager,
eventBeatManager: EventBeatManager,
componentFactory: ComponentFactory,
) {
fabricUIManager.setBinding(this)
installFabricUIManager(
runtimeExecutor,
runtimeScheduler,
fabricUIManager,
eventBeatManager,
componentFactory,
)
setPixelDensity(getDisplayMetricDensity())
}
// 省略部分代码......
}
该对象的构造,主要是调用initHybrid方法。关于initHybrid的机制,我们在前面的文章已经做了详细分析,这里就不再重复解释。
这里的FabricUIManagerBinding是 React Native Fabric 架构在 Android 端的 "核心启动器" 和 "跨语言胶水层"。它的主要作用是初始化 Fabric 的 C++ 核心组件,并建立 Java、C++ 和 JavaScript 三者之间的通信桥梁。当该对象被创建时,立即调用了其register方法。在这个方法中,主要是调用了installFabricUIManager,它将 C++ 层的 Fabric API 绑定到 JavaScript 运行时(Runtime)。这使得 JavaScript 可以直接通过 JSI 调用 C++ 接口(如 createNode, cloneNode, appendChild),实现同步且高效的 UI 操作。这里还有一个重要的操作,即setPixelDensity,将 Android 设备的屏幕像素密度(Density)传递给 React Native 的 C++ 核心层(Fabric/Yoga),用于统一布局单位。
最后,分析一下FabricUIManager的initialize做了什么事:
kotlin
public void initialize() {
// 注册事件批处理监听
mEventDispatcher.addBatchEventDispatchedListener(mBatchEventDispatchedListener);
// 启用 Fabric 日志与性能监控
if (ReactNativeFeatureFlags.enableFabricLogs()) {
mDevToolsReactPerfLogger = new DevToolsReactPerfLogger();
mDevToolsReactPerfLogger.addDevToolsReactPerfLoggerListener(FABRIC_PERF_LOGGER);
ReactMarker.addFabricListener(mDevToolsReactPerfLogger);
}
// 启用新旧架构互操作
if (ReactNativeNewArchitectureFeatureFlags.useFabricInterop()) {
InteropEventEmitter interopEventEmitter = new InteropEventEmitter(mReactApplicationContext);
mReactApplicationContext.internal_registerInteropModule(
RCTEventEmitter.class, interopEventEmitter);
}
}
这里首先是将 mBatchEventDispatchedListener(即 EventBeatManager)注册到事件分发器中。这是 "心跳" 连接的关键一步。当 Android 原生事件(如 Touch)被成批分发时,会通知 EventBeatManager,进而触发 C++ 层的 tick(),驱动 Fabric 渲染管线刷新。没有这一步,JavaScript 可能永远收不到事件更新。
接下来是性能监控相关的处理,开启需依赖enableFabricLogs的值,这是排查 Fabric 性能问题(如掉帧、白屏)和调试渲染流程的"开关"。
最后是启用新旧架构互操作的处理,这是 React Native 平滑迁移到新架构的重要兼容层,确保老代码在新架构下也能工作。
C++层
以上FabricUIManagerBinding提供了很多Native方法,我们在此重点分析一下installFabricUIManager。源码react-native/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp:
cpp
void FabricUIManagerBinding::installFabricUIManager(
jni::alias_ref<JRuntimeExecutor::javaobject> runtimeExecutorHolder,
jni::alias_ref<JRuntimeScheduler::javaobject> runtimeSchedulerHolder,
jni::alias_ref<JFabricUIManager::javaobject> javaUIManager,
EventBeatManager* eventBeatManager,
ComponentFactory* componentsRegistry) {
TraceSection s("FabricUIManagerBinding::installFabricUIManager");
enableFabricLogs_ = ReactNativeFeatureFlags::enableFabricLogs();
if (enableFabricLogs_) {
LOG(WARNING)
<< "FabricUIManagerBinding::installFabricUIManager() was called (address: "
<< this << ").";
}
std::unique_lock lock(installMutex_);
// 创建 C++ MountingManager (
auto globalJavaUiManager = make_global(javaUIManager);
mountingManager_ =
std::make_shared<FabricMountingManager>(globalJavaUiManager);
std::shared_ptr<const ContextContainer> contextContainer =
std::make_shared<ContextContainer>();
auto runtimeExecutor = runtimeExecutorHolder->cthis()->get();
auto runtimeScheduler = runtimeSchedulerHolder->cthis()->get().lock();
// 如果存在 RuntimeScheduler(通常都存在),则包装 runtimeExecutor。
// 这意味着所有通过此 executor 提交的 JS 任务都会经过 RuntimeScheduler 调度,从而支持优先级和任务取消。
if (runtimeScheduler) {
runtimeExecutor =
[runtimeScheduler](
std::function<void(jsi::Runtime & runtime)>&& callback) {
runtimeScheduler->scheduleWork(std::move(callback));
};
contextContainer->insert(
RuntimeSchedulerKey, std::weak_ptr<RuntimeScheduler>(runtimeScheduler));
}
// 创建 EventBeat 工厂
EventBeat::Factory eventBeatFactory =
[eventBeatManager, &runtimeScheduler, globalJavaUiManager](
std::shared_ptr<EventBeat::OwnerBox> ownerBox)
-> std::unique_ptr<EventBeat> {
return std::make_unique<AndroidEventBeat>(
std::move(ownerBox),
eventBeatManager,
*runtimeScheduler,
globalJavaUiManager);
};
contextContainer->insert("FabricUIManager", globalJavaUiManager);
// 组装 Scheduler 工具箱
auto toolbox = SchedulerToolbox{};
toolbox.contextContainer = contextContainer;
toolbox.componentRegistryFactory = componentsRegistry->buildRegistryFunction;
// TODO: (T132338609) runtimeExecutor 应该在主 bundle eval 之后执行 lambda 表达式,
// 而 bindingsInstallExecutor 应该在之前执行。
toolbox.bridgelessBindingsExecutor = std::nullopt;
toolbox.runtimeExecutor = runtimeExecutor;
toolbox.eventBeatFactory = eventBeatFactory;
// 启动 Fabric 核心
animationDriver_ = std::make_shared<LayoutAnimationDriver>(
runtimeExecutor, contextContainer, this);
scheduler_ =
std::make_shared<Scheduler>(toolbox, animationDriver_.get(), this);
}
这个方法是一个 "组装车间":
-
它接收来自 Android (Java) 的原材料(
UIManager,EventBeatManager)。 -
它接收来自 JS Runtime 的驱动器(
RuntimeExecutor)。 -
它将这些零件组装成 C++ 的核心部件(
MountingManager,AndroidEventBeat)。 -
最后,它启动了 Fabric 的引擎 ------
Scheduler。
总结
下面是对整个初始化流程的概述
ReactInstance 初始化
│
│
└─► Fabric 初始化
│
├─► ViewManagerResolver 创建 (收集 ReactPackage 中的 ViewManager)
│
├─► ViewManagerRegistry 创建
│
├─► FabricUIManager 创建
│
├─► FabricUIManagerBinding 创建
│
└─► binding.register() ──► 触发 C++ 层初始化
启动渲染
回顾一下Android 的初始化流程一文:
kotlin
// ReactInstance.kt
/**
* 渲染一个 React Native surface.
*
* @param surface 要渲染的 [com.facebook.react.interfaces.fabric.ReactSurface] 对象
*/
@ThreadConfined("ReactHost")
fun startSurface(surface: ReactSurfaceImpl) {
// 省略部分代码......
val view = surface.view
if (surface.isRunning) {
// Surface 已经在运行(预渲染过),只需附加 View
fabricUIManager.attachRootView(surface.surfaceHandler, view)
} else {
fabricUIManager.startSurface(surface.surfaceHandler, surface.context, view)
}
}
之前并未深入fabricUIManager.startSurface方法的调用,现在来分析一下:
java
// FabricUIManager.java
public void startSurface(
final SurfaceHandlerBinding surfaceHandler,
final Context context,
final @Nullable View rootView) {
final int rootTag =
rootView instanceof ReactRoot
? ((ReactRoot) rootView).getRootViewTag()
: ReactRootViewTagGenerator.getNextRootViewTag();
ThemedReactContext reactContext =
new ThemedReactContext(
mReactApplicationContext, context, surfaceHandler.getModuleName(), rootTag);
mMountingManager.startSurface(rootTag, reactContext, rootView);
Assertions.assertNotNull(mBinding, "Binding in FabricUIManager is null");
mBinding.startSurfaceWithSurfaceHandler(rootTag, surfaceHandler, rootView != null);
}
此方法主要做了两件事,首先是调用MountingManager 的startSurface启动 Surface,接着调用了FabricUIManagerBinding 的Native方法startSurfaceWithSurfaceHandler在C++层启动 Surface。先查看react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountingManager.kt:
kotlin
/**
* 启动 Surface 但不附加视图。对该 Surface 执行的所有视图操作都将被排队,直到视图被附加为止。
*/
@AnyThread
fun startSurface(
surfaceId: Int,
reactContext: ThemedReactContext?,
rootView: View?,
): SurfaceMountingManager {
// 创建一个新的 SurfaceMountingManager 实例,负责管理特定 Surface 的视图挂载操作
val surfaceMountingManager =
SurfaceMountingManager(
surfaceId,
jsResponderHandler, // JS 响应处理器(处理触摸事件响应)
viewManagerRegistry,
rootViewManager,
mountItemExecutor, // 挂载项执行器
checkNotNull(reactContext),
)
// 理论上这里可能存在竞态条件,如果 addRootView 从不同线程被调用两次,
// 虽然这种情况(可能)极不可能发生,而且很可能是一个错误。
// 这个防止竞态条件的逻辑是从旧代码继承而来的,我们不知道在实际中是否真的会发生
// 所以,我们现在记录软异常。这在调试模式下会崩溃,但在生产环境中不会。
surfaceIdToManager.putIfAbsent(surfaceId, surfaceMountingManager)
if (surfaceIdToManager[surfaceId] !== surfaceMountingManager) {
logSoftException(
TAG,
IllegalStateException(
"Called startSurface more than once for the SurfaceId [$surfaceId]"
),
)
}
mostRecentSurfaceMountingManager = surfaceIdToManager[surfaceId]
if (rootView != null) {
surfaceMountingManager.attachRootView(rootView, reactContext)
}
return surfaceMountingManager
}
此方法主要内容是创建SurfaceMountingManager ,然后调用attachRootView方法。现在继续跟踪attachRootView方法,源码react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java:
java
public void attachRootView(View rootView, ThemedReactContext themedReactContext) {
mThemedReactContext = themedReactContext;
addRootView(rootView);
}
private void addRootView(@NonNull final View rootView) {
if (isStopped()) {
return; // 检查 Surface 是否已停止
}
mTagToViewState.put(mSurfaceId, new ViewState(mSurfaceId, rootView, mRootViewManager, true));
// 在 UI 线程上执行根视图设置
Runnable runnable =
new GuardedRunnable(Assertions.assertNotNull(mThemedReactContext)) {
@Override
public void runGuarded() {
// 自从调用`addRootView`以来,CPU 一直在运行,因此从理论上讲,界面可能已经在此处停止渲染了。
if (isStopped()) {
return;
}
// 省略部分日志打印......
// 设置根视图 ID
rootView.setId(mSurfaceId);
if (rootView instanceof ReactRoot) {
((ReactRoot) rootView).setRootViewTag(mSurfaceId);
}
executeMountItemsOnViewAttach();
// 通过在调用 `executeMountItemsOnViewAttach` 之后执行此操作,
// 我们可以确保在处理此队列时安排的任何操作也会被添加到队列中,
// 而不是通过 `MountItemDispatcher` 中的队列立即进行处理。
mRootViewAttached = true;
}
};
// 确保在 UI 线程执行
if (UiThreadUtil.isOnUiThread()) {
runnable.run();
} else {
UiThreadUtil.runOnUiThread(runnable);
}
}
这里的实现核心是封装了一个Runnable ,即一个任务,且这个任务必须在安卓的UI线程执行。继续跟踪executeMountItemsOnViewAttach方法,查看任务的内容:
java
private final Queue<MountItem> mOnViewAttachMountItems = new ArrayDeque<>();
@UiThread
@ThreadConfined(UI)
private void executeMountItemsOnViewAttach() {
mMountItemExecutor.executeItems(mOnViewAttachMountItems);
}
可以看到,该方法就是在调用挂载项执行器不断的执行挂载项队列。这里的挂载项执行器是在创建MountingManager 时传入的,回到FabricUIManager源码查看实现:
java
private final MountingManager.MountItemExecutor mMountItemExecutor =
new MountingManager.MountItemExecutor() {
@Override
public void executeItems(Queue<MountItem> items) {
// 从技术上讲,在调度程序创建之前就可以访问这个执行器,但如果真的发生这种情况,那就说明出了非常严重的问题。
mMountItemDispatcher.dispatchMountItems(items);
}
};
继续跟踪react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/MountItemDispatcher.kt中的实现:
kotlin
@UiThread
@ThreadConfined(UI)
fun dispatchMountItems(mountItems: Queue<MountItem?>) {
while (!mountItems.isEmpty()) {
val item = requireNotNull(mountItems.poll()) { "MountItem should not be null" }
try {
item.execute(mountingManager)
} catch (e: RetryableMountingLayerException) {
// 省略已弃用的逻辑......
}
}
}
此处核心逻辑是从队列取出一个 MountItem 并执行它的execute方法。至于MountItem 接口,它有许多实现类,其中最核心的是IntBufferBatchMountItem实现,我们可以大致浏览一下它的execute方法主要做些什么。源码react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/mountitems/IntBufferBatchMountItem.kt:
kotlin
override fun execute(mountingManager: MountingManager) {
val surfaceMountingManager = mountingManager.getSurfaceManager(surfaceId)
if (surfaceMountingManager == null) {
return
}
if (surfaceMountingManager.isStopped) {
return
}
if (ReactNativeFeatureFlags.enableFabricLogs()) {
FLog.d(TAG, "Executing IntBufferBatchMountItem on surface [%d]", surfaceId)
}
beginMarkers("mountViews")
var i = 0
var j = 0
while (i < intBufferLen) {
val rawType = intBuffer[i++]
val type = rawType and INSTRUCTION_FLAG_MULTIPLE.inv()
val numInstructions =
(if ((rawType and INSTRUCTION_FLAG_MULTIPLE) != 0) intBuffer[i++] else 1)
val args = arrayOf("numInstructions", numInstructions.toString())
for (k in 0 until numInstructions) {
when (type) {
INSTRUCTION_CREATE -> {
val componentName = (objBuffer[j++] as String?).orEmpty()
val fabricComponentName =
FabricNameComponentMapping.getFabricComponentName(componentName)
// 创建视图
surfaceMountingManager.createView(
fabricComponentName,
intBuffer[i++],
objBuffer[j++] as ReadableMap?,
objBuffer[j++] as StateWrapper?,
objBuffer[j++] as EventEmitterWrapper?,
intBuffer[i++] == 1,
)
}
// 删除视图
INSTRUCTION_DELETE -> surfaceMountingManager.deleteView(intBuffer[i++])
INSTRUCTION_INSERT -> {
val tag = intBuffer[i++]
val parentTag = intBuffer[i++]
// 插入视图到父视图
surfaceMountingManager.addViewAt(parentTag, tag, intBuffer[i++])
}
// 从父视图移除
INSTRUCTION_REMOVE ->
surfaceMountingManager.removeViewAt(intBuffer[i++], intBuffer[i++], intBuffer[i++])
// 更新属性
INSTRUCTION_UPDATE_PROPS ->
surfaceMountingManager.updateProps(intBuffer[i++], objBuffer[j++] as ReadableMap?)
// 更新状态
INSTRUCTION_UPDATE_STATE ->
surfaceMountingManager.updateState(intBuffer[i++], objBuffer[j++] as StateWrapper?)
// 更新布局
INSTRUCTION_UPDATE_LAYOUT -> {
val reactTag = intBuffer[i++]
val parentTag = intBuffer[i++]
val x = intBuffer[i++]
val y = intBuffer[i++]
val width = intBuffer[i++]
val height = intBuffer[i++]
val displayType = intBuffer[i++]
val layoutDirection = intBuffer[i++]
surfaceMountingManager.updateLayout(
reactTag,
parentTag,
x,
y,
width,
height,
displayType,
layoutDirection,
)
}
// 省略部分代码......
else -> {
throw IllegalArgumentException(
"Invalid type argument to IntBufferBatchMountItem: $type at index: $i"
)
}
}
}
}
endMarkers()
}
可以看到,IntBufferBatchMountItem包含批量视图操作指令,它使用 int 数组和 object 数组优化性能,减少 JNI 调用,并且支持多种视图操作:创建、删除、插入、移除、更新属性/状态/布局等。
接下来我们跟踪一下surfaceMountingManager.createView的实现,源码react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/fabric/mounting/SurfaceMountingManager.java:
java
@UiThread
public void createView(
@NonNull String componentName,
int reactTag,
@Nullable ReadableMap props,
@Nullable StateWrapper stateWrapper,
@Nullable EventEmitterWrapper eventEmitterWrapper,
boolean isLayoutable) {
if (isStopped()) {
return;
}
ViewState viewState = getNullableViewState(reactTag);
if (viewState != null && viewState.mView != null) {
return;
}
createViewUnsafe(
componentName, reactTag, props, stateWrapper, eventEmitterWrapper, isLayoutable);
}
/**
* 执行视图创建操作,但不进行任何安全检查。您必须在调用此方法之前确保安全性(参见现有调用位置)
*/
@UiThread
public void createViewUnsafe(
@NonNull String componentName,
int reactTag,
@Nullable ReadableMap props,
@Nullable StateWrapper stateWrapper,
@Nullable EventEmitterWrapper eventEmitterWrapper,
boolean isLayoutable) {
Systrace.beginSection(
Systrace.TRACE_TAG_REACT,
"SurfaceMountingManager::createViewUnsafe(" + componentName + ")");
try {
ReactStylesDiffMap propMap = new ReactStylesDiffMap(props);
ViewState viewState = new ViewState(reactTag);
viewState.mCurrentProps = propMap;
viewState.mStateWrapper = stateWrapper;
viewState.mEventEmitter = eventEmitterWrapper;
mTagToViewState.put(reactTag, viewState);
if (isLayoutable) {
ViewManager viewManager = mViewManagerRegistry.get(componentName);
// View Managers are responsible for dealing with inital state and props.
viewState.mView =
viewManager.createView(
reactTag, mThemedReactContext, propMap, stateWrapper, mJSResponderHandler);
viewState.mViewManager = viewManager;
}
} finally {
Systrace.endSection(Systrace.TRACE_TAG_REACT);
}
}
这里是根据组件名通过mViewManagerRegistry来查找ViewManager。具体注册逻辑,我们在后面的注册组件一节分析。
继续跟踪一下viewManager.createView方法的实现,源码react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java:
java
/** 创建一个了解 props 和 state 的视图。 */
public @NonNull T createView(
int reactTag,
@NonNull ThemedReactContext reactContext,
@Nullable ReactStylesDiffMap props,
@Nullable StateWrapper stateWrapper,
JSResponderHandler jsResponderHandler) {
T view = createViewInstance(reactTag, reactContext, props, stateWrapper);
if (view instanceof ReactInterceptingViewGroup) {
((ReactInterceptingViewGroup) view).setOnInterceptTouchEventListener(jsResponderHandler);
}
return view;
}
/**
* 子类应该返回一个适当类型的新 View 实例。这是一个可选方法,它会为您调用 createViewInstance。
* 如果您需要在创建视图时使用 props 或 state,请重写它。
*
* 如果您重写此方法,您*必须*确保正确处理 updateProperties、view.setId、
* addEventEmitters 和 updateState/updateExtraData!
*
* @param reactTag 应该设置为视图实例 ID 的 reactTag
* @param reactContext 用于初始化视图实例的 ReactContext
* @param initialProps 视图实例的初始 props
* @param stateWrapper 视图实例的初始 state
*/
protected @NonNull T createViewInstance(
int reactTag,
@NonNull ThemedReactContext reactContext,
@Nullable ReactStylesDiffMap initialProps,
@Nullable StateWrapper stateWrapper) {
T view = null;
@Nullable Stack<T> recyclableViews = getRecyclableViewStack(reactContext.getSurfaceId(), true);
if (recyclableViews != null && !recyclableViews.empty()) {
T recyclableView = recyclableViews.pop();
// 当视图回收未对所有组件启用时,可回收视图仍可能附加到不可回收视图。
// 这保证了回收视图已从其父视图中移除。
if (ReactNativeFeatureFlags.enableViewRecycling() && recyclableView.getParent() != null) {
((ViewGroup) recyclableView.getParent()).removeView(recyclableView);
}
view = recycleView(reactContext, recyclableView);
} else {
view = createViewInstance(reactContext);
}
view.setId(reactTag);
addEventEmitters(reactContext, view);
if (initialProps != null) {
updateProperties(view, initialProps);
}
// 仅在 Fabric 架构中存在;但在 Fabric 中始终存在。
if (stateWrapper != null) {
Object extraData = updateState(view, initialProps, stateWrapper);
if (extraData != null) {
updateExtraData(view, extraData);
}
}
return view;
}
/**
* 子类应该返回一个适当类型的新 View 实例。
*/
protected abstract @NonNull T createViewInstance(@NonNull ThemedReactContext reactContext);
/**
* 子类可以重写此方法以在给定 View 上安装自定义事件发射器。
* 如果您的视图需要向 JS 发送除基本触摸事件之外的事件(例如滚动事件),
* 您可能想要重写此方法。
*/
protected void addEventEmitters(@NonNull ThemedReactContext reactContext, @NonNull T view) {}
/**
* 子类可以实现此方法以接收从 {@link ReactShadowNode#onCollectExtraUpdates} 中
* 相应 {@link ReactShadowNode} 实例排队的可选额外数据。
*
* 由于 CSS 布局步骤和 UI 更新可以在设置 x/y/width/height 之外的单独线程中执行,
* 这是从 CSS 节点向原生视图对应部分传递额外数据的推荐且线程安全的方式。
*
* <p>TODO T7247021: Replace updateExtraData with generic update props mechanism after D2086999
*/
public abstract void updateExtraData(@NonNull T view, Object extraData);
/**
* 子类可以实现此方法以接收在此组件类型的所有实例之间共享的状态更新。
*/
public @Nullable Object updateState(
@NonNull T view, ReactStylesDiffMap props, StateWrapper stateWrapper) {
return null;
}
这里比较重要的是createViewInstance方法,子类必须重写它,用于创建自定义View。现在查看一下我们创建的自定义Fabric 组件包中自动生成的模版代码:
kotlin
@ReactModule(name = CustomWebViewManager.NAME)
class CustomWebViewManager : SimpleViewManager<CustomWebView>(),
CustomWebViewManagerInterface<CustomWebView> {
private val mDelegate: ViewManagerDelegate<CustomWebView>
init {
mDelegate = CustomWebViewManagerDelegate(this)
}
override fun getDelegate(): ViewManagerDelegate<CustomWebView>? {
return mDelegate
}
override fun getName(): String {
return NAME
}
public override fun createViewInstance(context: ThemedReactContext): CustomWebView {
return CustomWebView(context)
}
@ReactProp(name = "sourceURL")
override fun setSourceURL(view: CustomWebView?, sourceURL: String?) {
// Implement the logic to handle the sourceURL property
}
companion object {
const val NAME = "CustomWebView"
}
}
可以看到,CustomWebViewManager实际上就是ViewManager的子孙类,其实现了createViewInstance方法,返回自定义的View实例。
这条线的跟踪已经足够深入了,关于原生UI组件的具体布局渲染就不继续了,先到此为止。
C++ 层
FabricUIManager 的startSurface方法中还有一个Native方法mBinding.startSurfaceWithSurfaceHandler未分析,源码react-native/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricUIManagerBinding.cpp:
cpp
void FabricUIManagerBinding::startSurfaceWithSurfaceHandler(
jint surfaceId,
jni::alias_ref<SurfaceHandlerBinding::jhybridobject> surfaceHandlerBinding,
jboolean isMountable) {
// SurfaceHandler配置
// 从JNI包装中提取C++ SurfaceHandler对象
const auto& surfaceHandler =
surfaceHandlerBinding->cthis()->getSurfaceHandler();
surfaceHandler.setSurfaceId(surfaceId);
// 根据是否有View设置显示模式
surfaceHandler.setDisplayMode(
isMountable != 0 ? DisplayMode::Visible : DisplayMode::Suspended);
// 获取Fabric调度器
auto scheduler = getScheduler();
if (!scheduler) {
LOG(ERROR)
<< "FabricUIManagerBinding::startSurfaceWithSurfaceHandler: scheduler disappeared";
return;
}
// 将SurfaceHandler注册到调度器中。调度器负责管理渲染和布局
scheduler->registerSurface(surfaceHandler);
auto mountingManager = getMountingManager("startSurfaceWithSurfaceHandler");
if (mountingManager != nullptr) {
// 通知MountingManager, Surface开始启动
// MountingManager负责将C++的UI操作转换为Android原生View操作
mountingManager->onSurfaceStart(surfaceId);
}
// 启动SurfaceHandler
surfaceHandler.start();
// 如果启用布局动画,设置动画驱动器
if (ReactNativeFeatureFlags::enableLayoutAnimationsOnAndroid()) {
surfaceHandler.getMountingCoordinator()->setMountingOverrideDelegate(
animationDriver_);
}
{
std::unique_lock lock(surfaceHandlerRegistryMutex_);
// 将SurfaceHandler添加到注册表中,便于后续管理(停止、更新等)
surfaceHandlerRegistry_.emplace(
surfaceId, jni::make_weak(surfaceHandlerBinding));
}
}
这个方法是新架构Surface启动的关键桥梁,它体现了新架构的核心特点:通过JSI直接从C++调用JS。
-
向上:接收Java层的启动请求
-
向下:触发C++层的Surface启动流程
-
横向:协调Scheduler、MountingManager等各个子系统
这里重点关注一下surfaceHandler.start()的实现,源码react-native/packages/react-native/ReactCommon/react/renderer/scheduler/SurfaceHandler.cpp:
cpp
void SurfaceHandler::start() const noexcept {
std::unique_lock lock(linkMutex_);
// 省略断言......
auto parameters = Parameters{};
{
std::shared_lock parametersLock(parametersMutex_);
parameters = parameters_;
}
// 创建ShadowTree
auto shadowTree = std::make_unique<ShadowTree>(
parameters.surfaceId,
parameters.layoutConstraints,
parameters.layoutContext,
*link_.uiManager,
*parameters.contextContainer);
// 将ShadowTree指针保存到link_中,供后续操作使用
link_.shadowTree = shadowTree.get();
if (!parameters.moduleName.empty()) {
// 启动Surface
link_.uiManager->startSurface(
std::move(shadowTree),
parameters.moduleName,
parameters.props,
parameters_.displayMode);
} else {
// 创建空Surface,仅用于预渲染
link_.uiManager->startEmptySurface(std::move(shadowTree));
}
// 将状态从Registered更新为Running
link_.status = Status::Running;
// 应用显示模式
applyDisplayMode(parameters.displayMode);
}
此方法是Fabric架构中渲染流程的起点,负责创建ShadowTree并启动UI渲染流程。这里的ShadowTree就是Fabric中的虚拟DOM树,用于布局计算和渲染。
继续跟踪uiManager->startSurface的实现,源码react-native/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp:
cpp
void UIManager::startSurface(
ShadowTree::Unique&& shadowTree,
const std::string& moduleName,
const folly::dynamic& props,
DisplayMode displayMode) const noexcept {
TraceSection s("UIManager::startSurface");
// ShadowTree注册
auto surfaceId = shadowTree->getSurfaceId();
shadowTreeRegistry_.add(std::move(shadowTree));
// 委托通知
// 使用访问者模式安全访问已注册的ShadowTree
shadowTreeRegistry_.visit(
surfaceId, [delegate = delegate_](const ShadowTree& shadowTree) {
if (delegate != nullptr) {
// 通知UIManager的委托对象Surface已启动
delegate->uiManagerDidStartSurface(shadowTree);
}
});
// 异步调用JS层
runtimeExecutor_([=](jsi::Runtime& runtime) {
TraceSection s("UIManager::startSurface::onRuntime");
// 在JS线程上异步执行
AppRegistryBinding::startSurface(
runtime, surfaceId, moduleName, props, displayMode);
});
}
此方法是Fabric架构中连接C++渲染系统和JS应用层的关键桥梁方法,负责注册ShadowTree并触发JS应用启动。
继续跟踪AppRegistryBinding::startSurface。源码react-native/packages/react-native/ReactCommon/react/renderer/uimanager/AppRegistryBinding.cpp:
cpp
/* static */ void AppRegistryBinding::startSurface(
jsi::Runtime& runtime,
SurfaceId surfaceId,
const std::string& moduleName,
const folly::dynamic& initialProps,
DisplayMode displayMode) {
TraceSection s("AppRegistryBinding::startSurface");
// 构建参数对象
jsi::Object parameters(runtime);
parameters.setProperty(runtime, "rootTag", surfaceId);
parameters.setProperty(
runtime, "initialProps", jsi::valueFromDynamic(runtime, initialProps));
parameters.setProperty(runtime, "fabric", true);
// 获取全局AppRegistry
// 访问JS全局对象
auto global = runtime.global();
// 查找RN$AppRegistry(在AppRegistry.js中设置)
auto registry = global.getProperty(runtime, "RN$AppRegistry");
if (!registry.isObject()) {
throw std::runtime_error(
"AppRegistryBinding::startSurface failed. Global was not installed.");
}
// 获取runApplication方法对象
auto method = std::move(registry).asObject(runtime).getPropertyAsFunction(
runtime, "runApplication");
// 调用JS方法:runApplication
method.call(
runtime,
{jsi::String::createFromUtf8(runtime, moduleName),
std::move(parameters),
jsi::Value(runtime, displayModeToInt(displayMode))});
}
这里的重点是就是JS层的runApplication方法调用,可以说这就是JS层的入口方法。此处的JSI C++调用等价于以下JS代码:
js
AppRegistry.runApplication(
"RNTesterApp", // moduleName
{
rootTag: 1, // surfaceId
initialProps: {...}, // 初始属性
fabric: true // 新架构标记
},
0 // displayMode
);
AppRegistryBinding::startSurface方法是新架构调用链的终点,负责通过JSI直接调用JS层的AppRegistry.runApplication,启动React Native应用。
渲染调度
接下来我们探索一下前面提到的mOnViewAttachMountItems队列:
java
// SurfaceMountingManager.java
@UiThread
@ThreadConfined(UI)
public void scheduleMountItemOnViewAttach(MountItem item) {
mOnViewAttachMountItems.add(item);
}
经过搜索,可以定位到scheduleMountItemOnViewAttach调用处:
kotlin
// MountItemDispatcher.kt
private fun getAndResetViewCommandMountItems(): List<DispatchCommandMountItem>? =
drainConcurrentItemQueue(viewCommandMountItems)
private fun getAndResetMountItems(): List<MountItem>? = drainConcurrentItemQueue(mountItems)
private fun getAndResetPreMountItems(): List<MountItem>? = drainConcurrentItemQueue(preMountItems)
private fun executeOrEnqueue(item: MountItem) {
if (mountingManager.isWaitingForViewAttach(item.getSurfaceId())) {
// Surface 还未准备好,将任务加入等待队列
val surfaceMountingManager: SurfaceMountingManager =
mountingManager.getSurfaceManagerEnforced(
item.getSurfaceId(),
"MountItemDispatcher::executeOrEnqueue",
)
surfaceMountingManager.scheduleMountItemOnViewAttach(item)
} else {
// Surface 已准备好,直接执行
item.execute(mountingManager)
}
}
/*
* 按以下顺序执行视图命令、预挂载项和挂载项:
* 1. 视图命令
* 2. 预挂载项
* 3. 常规挂载项
*
* 如果 `viewCommandMountItemsToDispatch` 和 `mountItemsToDispatch` 都为空,则不执行任何操作。
* 除了 `tryDispatchMountItems` 之外,任何地方都不应直接调用此方法。
*/
@UiThread
@ThreadConfined(UI)
private fun dispatchMountItems() {
batchedExecutionTime = 0
runStartTime = SystemClock.uptimeMillis()
// 初始化和获取待分发项
val viewCommandMountItemsToDispatch = getAndResetViewCommandMountItems()
val mountItemsToDispatch = getAndResetMountItems()
if (mountItemsToDispatch == null && viewCommandMountItemsToDispatch == null) {
return
}
itemDispatchListener.willMountItems(mountItemsToDispatch)
// 定义视图命令分发函数
val dispatchViewCommand: (command: DispatchCommandMountItem) -> Unit = { command ->
if (ReactNativeFeatureFlags.enableFabricLogs()) {
printMountItem(command, "dispatchMountItems: Executing viewCommandMountItem")
}
try {
executeOrEnqueue(command)
} catch (e: RetryableMountingLayerException) {
// 省略......
} catch (e: Throwable) {
// 省略......
}
}
// 执行ViewCommand
// 作为优化,首先执行所有 ViewCommand
// 这应该是:
// 1) 高性能的:ViewCommand 通常是 SetNativeProps 的替代品,我们一直希望它尽可能"同步"。
// 2) 更安全的:ViewCommand 本质上与树提交/差异/挂载过程断开连接。
// JS 令式地排队这些命令。
// 如果 JS 已经排队了一个命令,可以合理地假设时间过得越久, 视图消失的可能性就越大。
// 因此,通过提前执行 ViewCommand,我们实际上应该避免一类错误/故障。
viewCommandMountItemsToDispatch?.let { commands ->
for (command in commands) {
dispatchViewCommand(command)
}
Systrace.endSection(Systrace.TRACE_TAG_REACT)
}
// 执行PreMountItem
// 如果有 MountItem 要分发,我们确保所有"预挂载项"首先执行
getAndResetPreMountItems()?.let { preMountItems ->
for (preMountItem in preMountItems) {
if (ReactNativeFeatureFlags.enableFabricLogs()) {
printMountItem(preMountItem, "dispatchMountItems: Executing preMountItem")
}
executeOrEnqueue(preMountItem)
}
Systrace.endSection(Systrace.TRACE_TAG_REACT)
}
// 执行常规 MountItem
mountItemsToDispatch?.let { items ->
val batchedExecutionStartTime = SystemClock.uptimeMillis()
for (mountItem in items) {
if (ReactNativeFeatureFlags.enableFabricLogs()) {
printMountItem(mountItem, "dispatchMountItems: Executing mountItem")
}
val command = mountItem as? DispatchCommandMountItem
if (command != null) {
dispatchViewCommand(command)
continue
}
try {
executeOrEnqueue(mountItem)
} catch (e: Throwable) {
// 省略......
}
}
batchedExecutionTime += SystemClock.uptimeMillis() - batchedExecutionStartTime
}
itemDispatchListener.didMountItems(mountItemsToDispatch)
}
/**
* 尝试分发 MountItems。如果出现异常,我们将重试 10 次后放弃。
*/
fun tryDispatchMountItems() {
// 如果我们已经在分发中,不要重入。在 Android 的 Fabric 中可能经常发生重入,
// 因为来自挂载层的 `updateState` 会导致挂载项被同步分发。我们想要
// 1)确保在这些情况下不重入,但 2)仍然同步执行这些排队的指令。
// 这是一个相当粗暴的工具,但我们可能没有更好的选择,因为我们真的不想执行任何乱序的操作。
if (inDispatch) {
return
}
inDispatch = true
try {
dispatchMountItems()
} finally {
// 即使抛出异常也要在运行 dispatchMountItems 后清理
inDispatch = false
}
// 无论我们是否实际分发了任何内容,我们都会调用 didDispatchMountItems,
// 因为 NativeAnimatedModule 依赖于此来执行可能已调度的任何动画
itemDispatchListener.didDispatchMountItems()
}
可以看到,scheduleMountItemOnViewAttach仅被executeOrEnqueue方法调用。只有在Surface 还未准备好时,才将任务加入等待队列。否则,直接在executeOrEnqueue中执行挂载项。
这里executeOrEnqueue方法在dispatchMountItems方法中有三次调用,分别对应着三个队列。这三个队列存在执行顺序:
-
ViewCommand 优先执行:提高性能和安全性(存储视图命令,如 scrollTo、focus等,这些是来自 JS 的命令式调用,与视图树的提交/差异/挂载过程分离)
-
PreMountItem 次之:为后续挂载做准备(存储预挂载项,主要是视图预分配操作。这是性能优化,尽可能提前完成工作)
-
常规 MountItem 最后:执行实际的视图操作(存储常规挂载项,包含主要的视图操作。这是视图树更新的核心操作)
有个地方需要注意,就是drainConcurrentItemQueue方法。此方法是一次性清空队列,将结果转为一个List。之所以这样做,是为了将累积的所有挂载项一次性取出进行批处理,避免在执行过程中队列继续增长导致的不一致性。这里的三个队列都调用了此方法返回列表。
接下来,根据注释可知,dispatchMountItems方法只应该被tryDispatchMountItems调用。而tryDispatchMountItems方法主要做了一个防止重入的处理。继续搜索该方法,发现只有两处调用,且都在FabricUIManager.java中:
java
private class DispatchUIFrameCallback extends GuardedFrameCallback {
private volatile boolean mIsMountingEnabled = true;
@ThreadConfined(UI)
private boolean mShouldSchedule = false;
@ThreadConfined(UI)
private boolean mIsScheduled = false;
private DispatchUIFrameCallback(ReactContext reactContext) {
super(reactContext);
}
@UiThread
@ThreadConfined(UI)
private void schedule() {
if (!mIsScheduled && mShouldSchedule) {
mIsScheduled = true;
ReactChoreographer.getInstance()
.postFrameCallback(ReactChoreographer.CallbackType.DISPATCH_UI, this);
}
}
// 省略部分代码......
@Override
@UiThread
@ThreadConfined(UI)
public void doFrameGuarded(long frameTimeNanos) {
mIsScheduled = false;
if (!mIsMountingEnabled) {
return;
}
if (mDestroyed) {
return;
}
// 通过 C++ 驱动所有动画。
// 这里存在获取/设置 `mDriveCxxAnimations` 的竞态条件,但这应该无关紧要;
// 调用 mBinding 方法是安全的,除非 mBinding 对象已被销毁。
if ((mDriveCxxAnimations || ReactNativeFeatureFlags.cxxNativeAnimatedEnabled())
&& mBinding != null) {
mBinding.driveCxxAnimations();
}
if (mBinding != null) {
mBinding.drainPreallocateViewsQueue();
}
try {
// 首先,在 frameTimeNanos 时间内执行尽可能多的预挂载项。
// 如果不是所有预挂载项都被执行,可能发生以下情况:
// 1. 如果 MountItemDispatcher 中有视图命令或挂载项:执行剩余的预挂载项。
// 2. 如果没有视图命令或挂载项,等待下一帧。
mMountItemDispatcher.dispatchPreMountItems(frameTimeNanos);
mMountItemDispatcher.tryDispatchMountItems();
} catch (Exception ex) {
FLog.e(TAG, "Exception thrown when executing UIFrameGuarded", ex);
mIsMountingEnabled = false;
throw ex;
} finally {
schedule();
}
mSynchronousEvents.clear();
}
}
先看doFrameGuarded这处调用,这里正是一开始分析初始化流程时提到的基于 Android Choreographer帧回调处理。
-
监听 VSync 信号
-
在每帧开始时触发回调
-
按优先级顺序执行回调队列
-
mBinding.driveCxxAnimations()- C++ 动画 -
mBinding.drainPreallocateViewsQueue()- C++ 预分配队列 -
dispatchPreMountItems(frameTimeNanos)- Java 预挂载项 -
tryDispatchMountItems()- 常规挂载项
-
doFrameGuarded这处调用就是正常的帧驱动渲染流程,确保 UI 更新与屏幕刷新同步。
我们再看另一处调用:
java
/**
* 此方法将 UI 操作直接添加到 UI 线程队列中。以使用 {@link ReactChoreographer.CallbackType} 来强制执行顺序。
* 此方法应仅在提交新渲染树后调用。
*/
@SuppressWarnings("unused")
@AnyThread
@ThreadConfined(ANY)
private void scheduleMountItem(
@Nullable final MountItem mountItem,
int commitNumber,
long commitStartTime,
long diffStartTime,
long diffEndTime,
long layoutStartTime,
long layoutEndTime,
long finishTransactionStartTime,
long finishTransactionEndTime,
int affectedLayoutNodesCount) {
// 当 Binding.cpp 在提交阶段调用 scheduleMountItems 时,它总是使用 BatchMountItem 调用。
// 没有其他地方使用 BatchMountItem 调用此方法,并且 Binding.cpp 只使用 BatchMountItem 调用 scheduleMountItems。
long scheduleMountItemStartTime = SystemClock.uptimeMillis();
boolean isBatchMountItem = mountItem instanceof BatchMountItem;
boolean shouldSchedule = false;
// 判断是否为批量挂载项
if (isBatchMountItem) {
BatchMountItem batchMountItem = (BatchMountItem) mountItem;
Assertions.assertNotNull(batchMountItem, "BatchMountItem is null");
shouldSchedule = !batchMountItem.isBatchEmpty();
} else {
shouldSchedule = mountItem != null;
}
// 省略性能记录......
// 通知监听器
// 在同步渲染的情况下,这可能在 UI 线程上被调用。否则,
// 它应该几乎总是在 JS 线程上被调用。
for (UIManagerListener listener : mListeners) {
listener.didScheduleMountItems(this);
}
// 调度执行
if (shouldSchedule) {
Assertions.assertNotNull(mountItem, "MountItem is null");
// 将 MountItem 添加到分发器队列
mMountItemDispatcher.addMountItem(mountItem);
// 判断是否是UI线程,如果是UI线程则立即执行,实现低延迟渲染
if (UiThreadUtil.isOnUiThread()) {
Runnable runnable =
new GuardedRunnable(mReactApplicationContext) {
@Override
public void runGuarded() {
mMountItemDispatcher.tryDispatchMountItems();
}
};
runnable.run();
}
}
// 在锁外和同步挂载完成执行后发布标记
if (isBatchMountItem) {
// 省略部分代码......
}
}
可以看到,此方法是由C++层调用,在其中实现了双路径执行渲染策略。当前如果在UI线程调用,则会立即执行渲染。反之,只是通过mMountItemDispatcher.addMountItem(mountItem)将 MountItem 添加到队列,等待帧回调处理:
kotlin
// MountItemDispatcher.kt
fun addMountItem(mountItem: MountItem) {
mountItems.add(mountItem)
}
C++ 层
现在来研究一下C++层触发scheduleMountItem的流程,源码react-native/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp:
cpp
jsi::Value UIManagerBinding::get(
jsi::Runtime& runtime,
const jsi::PropNameID& name) {
auto methodName = name.utf8(runtime);
UIManager* uiManager = uiManager_.get();
// 省略部分代码......
if (methodName == "completeRoot") {
auto paramCount = 2;
return jsi::Function::createFromHostFunction(
runtime,
name,
paramCount,
[uiManager, methodName, paramCount](
jsi::Runtime& runtime,
const jsi::Value& /*thisValue*/,
const jsi::Value* arguments,
size_t count) -> jsi::Value {
validateArgumentCount(runtime, methodName, paramCount, count);
auto runtimeSchedulerBinding =
RuntimeSchedulerBinding::getBinding(runtime);
auto surfaceId = surfaceIdFromValue(runtime, arguments[0]);
auto shadowNodeList = shadowNodeListFromValue(runtime, arguments[1]);
uiManager->completeSurface(
surfaceId,
shadowNodeList,
{.enableStateReconciliation = true,
.mountSynchronously = false,
.source = ShadowTree::CommitSource::React});
return jsi::Value::undefined();
});
}
// 省略部分代码......
return jsi::Value::undefined();
}
继续跟踪completeSurface方法实现,源码react-native/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp:
cpp
void UIManager::completeSurface(
SurfaceId surfaceId,
const ShadowNode::UnsharedListOfShared& rootChildren,
ShadowTree::CommitOptions commitOptions) {
TraceSection s("UIManager::completeSurface", "surfaceId", surfaceId);
shadowTreeRegistry_.visit(surfaceId, [&](const ShadowTree& shadowTree) {
auto result = shadowTree.commit(
[&](const RootShadowNode& oldRootShadowNode) {
return std::make_shared<RootShadowNode>(
oldRootShadowNode,
ShadowNodeFragment{
.props = ShadowNodeFragment::propsPlaceholder(),
.children = rootChildren,
});
},
commitOptions);
if (result == ShadowTree::CommitStatus::Succeeded) {
// It's safe to update the visible revision of the shadow tree immediately
// after we commit a specific one.
lazyShadowTreeRevisionConsistencyManager_->updateCurrentRevision(
surfaceId, shadowTree.getCurrentRevision().rootShadowNode);
}
});
}
这里的核心是ShadowTree::commit方法的调用,实参是个闭包。它主要用于计算 Diff,生成 MountingTransaction。继续跟踪源码react-native/packages/react-native/ReactCommon/react/renderer/mounting/ShadowTree.cpp:
cpp
CommitStatus ShadowTree::commit(
const ShadowTreeCommitTransaction& transaction,
const CommitOptions& commitOptions) const {
[[maybe_unused]] int attempts = 0;
if (ReactNativeFeatureFlags::preventShadowTreeCommitExhaustion()) {
while (attempts < MAX_COMMIT_ATTEMPTS_BEFORE_LOCKING) {
auto status = tryCommit(transaction, commitOptions);
if (status != CommitStatus::Failed) {
return status;
}
attempts++;
}
{
std::unique_lock lock(commitMutexRecursive_);
return tryCommit(transaction, commitOptions);
}
} else {
// 循环尝试提交,直到成功或达到最大尝试次数
while (true) {
attempts++;
auto status = tryCommit(transaction, commitOptions);
if (status != CommitStatus::Failed) {
return status;
}
// After multiple attempts, we failed to commit the transaction.
// Something internally went terribly wrong.
react_native_assert(attempts < 1024);
}
}
}
CommitStatus ShadowTree::tryCommit(
const ShadowTreeCommitTransaction& transaction,
const CommitOptions& commitOptions) const {
TraceSection s("ShadowTree::commit");
auto telemetry = TransactionTelemetry{};
telemetry.willCommit();
CommitMode commitMode;
auto oldRevision = ShadowTreeRevision{};
auto newRevision = ShadowTreeRevision{};
{
// Reading `currentRevision_` in shared manner.
SharedLock lock = sharedCommitLock();
commitMode = commitMode_;
oldRevision = currentRevision_;
}
const auto& oldRootShadowNode = oldRevision.rootShadowNode;
// 1. 执行 transaction,生成新的 RootShadowNode
auto newRootShadowNode = transaction(*oldRevision.rootShadowNode);
if (!newRootShadowNode) {
return CommitStatus::Cancelled;
}
// 2. 状态协调(State Reconciliation)
if (commitOptions.enableStateReconciliation) {
auto updatedNewRootShadowNode =
progressState(*newRootShadowNode, *oldRootShadowNode);
if (updatedNewRootShadowNode) {
newRootShadowNode =
std::static_pointer_cast<RootShadowNode>(updatedNewRootShadowNode);
}
}
// 3. 调用 delegate 的 shadowTreeWillCommit hook
newRootShadowNode = delegate_.shadowTreeWillCommit(
*this, oldRootShadowNode, newRootShadowNode, commitOptions);
if (!newRootShadowNode) {
return CommitStatus::Cancelled;
}
// Layout nodes.
std::vector<const LayoutableShadowNode*> affectedLayoutableNodes{};
affectedLayoutableNodes.reserve(1024);
telemetry.willLayout();
telemetry.setAsThreadLocal();
// 4. 布局计算
newRootShadowNode->layoutIfNeeded(&affectedLayoutableNodes);
telemetry.unsetAsThreadLocal();
telemetry.didLayout(static_cast<int>(affectedLayoutableNodes.size()));
{
// Updating `currentRevision_` in unique manner if it hasn't changed.
UniqueLock lock = uniqueCommitLock();
if (currentRevision_.number != oldRevision.number) {
return CommitStatus::Failed;
}
auto newRevisionNumber = currentRevision_.number + 1;
{
std::scoped_lock dispatchLock(EventEmitter::DispatchMutex());
updateMountedFlag(
currentRevision_.rootShadowNode->getChildren(),
newRootShadowNode->getChildren(),
commitOptions.source);
}
telemetry.didCommit();
telemetry.setRevisionNumber(static_cast<int>(newRevisionNumber));
// Seal the shadow node so it can no longer be mutated
// Does nothing in release.
newRootShadowNode->sealRecursive();
newRevision = ShadowTreeRevision{
.rootShadowNode = std::move(newRootShadowNode),
.number = newRevisionNumber,
.telemetry = telemetry};
// 5. 更新 currentRevision_
currentRevision_ = newRevision;
}
// 6. 发送布局事件
emitLayoutEvents(affectedLayoutableNodes);
// 7. 关键:如果 commitMode 是 Normal,调用 mount
if (commitMode == CommitMode::Normal) {
mount(std::move(newRevision), commitOptions.mountSynchronously);
}
return CommitStatus::Succeeded;
}
void ShadowTree::mount(ShadowTreeRevision revision, bool mountSynchronously)
const {
// 1. 将新的 revision 推送到 MountingCoordinator
mountingCoordinator_->push(std::move(revision));
// 2. 调用 delegate 的 shadowTreeDidFinishTransaction
delegate_.shadowTreeDidFinishTransaction(
mountingCoordinator_, mountSynchronously);
}
注意,ShadowTreeRevision 表示 Shadow Tree 的一个已提交快照(版本)。由三个组成部分:
-
rootShadowNode:整个 Shadow Tree 的根节点,包含完整的树结构和所有子节点 -
number:版本号,从 0(INITIAL_REVISION)开始递增,每次 commit 创建新 revision 时递增。主要用于检测并发冲突和版本追踪 -
telemetry:性能数据(commit 时间、layout 时间、影响的节点数等
MountingCoordinator 使用 revision 来计算新旧树之间的差异,生成需要执行的视图操作。
继续跟踪shadowTreeDidFinishTransaction的实现:
cpp
// UIManager.cpp
void UIManager::shadowTreeDidFinishTransaction(
std::shared_ptr<const MountingCoordinator> mountingCoordinator,
bool mountSynchronously) const {
TraceSection s("UIManager::shadowTreeDidFinishTransaction");
if (delegate_ != nullptr) {
delegate_->uiManagerDidFinishTransaction(
std::move(mountingCoordinator), mountSynchronously);
}
}
继续跟踪uiManagerDidFinishTransaction实现,源码react-native/packages/react-native/ReactCommon/react/renderer/scheduler/Scheduler.cpp:
cpp
void Scheduler::uiManagerDidFinishTransaction(
std::shared_ptr<const MountingCoordinator> mountingCoordinator,
bool mountSynchronously) {
TraceSection s("Scheduler::uiManagerDidFinishTransaction");
if (delegate_ != nullptr) {
// 除了 Android 平台之外,此操作在所有平台上均无效,
// 因为在 Android 平台上我们需要观察每个事务才能正确地进行挂载。
delegate_->schedulerDidFinishTransaction(mountingCoordinator);
if (!mountSynchronously) {
auto surfaceId = mountingCoordinator->getSurfaceId();
runtimeScheduler_->scheduleRenderingUpdate(
surfaceId,
[delegate = delegate_,
mountingCoordinator = std::move(mountingCoordinator)]() {
delegate->schedulerShouldRenderTransactions(mountingCoordinator);
});
} else {
delegate_->schedulerShouldRenderTransactions(mountingCoordinator);
}
}
}
继续跟踪schedulerShouldRenderTransactions实现:
cpp
// FabricUIManagerBinding.cpp
void FabricUIManagerBinding::schedulerShouldRenderTransactions(
const std::shared_ptr<const MountingCoordinator>& mountingCoordinator) {
auto mountingManager =
getMountingManager("schedulerShouldRenderTransactions");
if (!mountingManager) {
return;
}
if (ReactNativeFeatureFlags::enableAccumulatedUpdatesInRawPropsAndroid()) {
auto mountingTransaction = mountingCoordinator->pullTransaction(
/* willPerformAsynchronously = */ true);
if (mountingTransaction.has_value()) {
auto transaction = std::move(*mountingTransaction);
mountingManager->executeMount(transaction);
}
} else {
std::vector<MountingTransaction> pendingTransactions;
{
// 保留锁以访问待处理事务,但不要执行挂载操作,因为该方法可能会再次调用此方法。
//
// 当挂载管理器同步触发状态更新时(例如从 UI 线程提交时),此方法可能会被重入调用。
// 这是安全的,因为我们已经将同一surface ID 的所有事务合并到待处理事务列表中的单个事务中,
// 因此操作不会乱序执行。
std::unique_lock<std::mutex> lock(pendingTransactionsMutex_);
pendingTransactions_.swap(pendingTransactions);
}
for (auto& transaction : pendingTransactions) {
mountingManager->executeMount(transaction);
}
}
}
到这里,最重要的方法是executeMount。该方法负责将 C++ 层的 Shadow Tree 变更(MountingTransaction)转换为 Java 层的视图操作指令,并调度到 UI 线程执行。
具体说,就是将 C++ 的 ShadowViewMutation 转换为 Java 的 MountItem,并序列化传递给 Java 层。该方法的实现非常长,这里仅摘要一部分。源码react-native/packages/react-native/ReactAndroid/src/main/jni/react/fabric/FabricMountingManager.cpp:
cpp
void FabricMountingManager::executeMount(
const MountingTransaction& transaction) {
// 省略......
// 遍历 mutations,将 ShadowViewMutation(Create/Delete/Insert/Remove/Update)转换为 CppMountItem
for (const auto& mutation : mutations) {
auto parentTag = mutation.parentTag;
const auto& oldChildShadowView = mutation.oldChildShadowView;
const auto& newChildShadowView = mutation.newChildShadowView;
auto& mutationType = mutation.type;
auto& index = mutation.index;
bool isVirtual = mutation.mutatedViewIsVirtual();
switch (mutationType) {
case ShadowViewMutation::Create: {
bool shouldCreateView =
!allocatedViewTags.contains(newChildShadowView.tag);
if (shouldCreateView) {
cppCommonMountItems.push_back(
CppMountItem::CreateMountItem(newChildShadowView));
allocatedViewTags.insert(newChildShadowView.tag);
}
break;
}
case ShadowViewMutation::Remove: {
if (!isVirtual) {
cppCommonMountItems.push_back(
CppMountItem::RemoveMountItem(
parentTag, oldChildShadowView, index));
}
break;
}
case ShadowViewMutation::Delete: {
(maintainMutationOrder ? cppCommonMountItems : cppDeleteMountItems)
.push_back(CppMountItem::DeleteMountItem(oldChildShadowView));
if (allocatedViewTags.erase(oldChildShadowView.tag) != 1) {
LOG(ERROR) << "Emitting delete for unallocated view "
<< oldChildShadowView.tag;
}
break;
}
// 省略代码......
default: {
break;
}
}
}
}
// 现在我们已经掌握了所有必要的信息,包括挂载项的顺序,因此可以准确地知道需要分配多少空间。
auto [batchMountItemIntsSize, batchMountItemObjectsSize] = computeBufferSizes(
cppCommonMountItems,
cppDeleteMountItems,
cppUpdatePropsMountItems,
cppUpdateStateMountItems,
cppUpdatePaddingMountItems,
cppUpdateLayoutMountItems,
cppUpdateOverflowInsetMountItems,
cppUpdateEventEmitterMountItems);
static auto scheduleMountItem = JFabricUIManager::javaClassStatic()
->getMethod<void(
JMountItem::javaobject,
jint,
jlong,
jlong,
jlong,
jlong,
jlong,
jlong,
jlong,
jint)>("scheduleMountItem");
// 省略
// 调用 JFabricUIManager.createIntBufferBatchMountItem() 创建 Java 对象
// 将序列化数据传递给 Java 层
static auto createMountItemsIntBufferBatchContainer =
JFabricUIManager::javaClassStatic()
->getMethod<jni::alias_ref<JMountItem>(
jint, jintArray, jni::jtypeArray<jobject>, jint)>(
"createIntBufferBatchMountItem");
auto batch = createMountItemsIntBufferBatchContainer(
javaUIManager_,
surfaceId,
// If there are no items, we pass a nullptr instead of passing the
// object through the JNI
batchMountItemIntsSize > 0 ? buffer.ints : nullptr,
batchMountItemObjectsSize > 0 ? buffer.objects.get() : nullptr,
revisionNumber);
auto finishTransactionEndTime = telemetryTimePointNow();
// 调度到 UI 线程,将 BatchMountItem 发送到 Java 层
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),
telemetry.getAffectedLayoutNodesCount());
env->DeleteLocalRef(buffer.ints);
}
可以看到,在此方法中反射调用了上层的scheduleMountItem,与我们前面分析的结果对接上了。
总的来说,该方法充当 C++ Shadow Tree 与 Java 原生视图之间的桥梁,将 React 的声明式变更转换为 Android 视图操作指令,并调度到 UI 线程执行,实现从虚拟树到真实视图的映射。
声明组件
要开发一个Fabric 组件,首先需要在TS代码中声明规范。我们使用npx create-react-native-library@latest创建一个Fabric组件库,然后参考根据官方文档的示例添加如下内容:
ts
import type {
CodegenTypes,
HostComponent,
ViewProps,
} from 'react-native';
import {codegenNativeComponent} from 'react-native';
type WebViewScriptLoadedEvent = {
result: 'success' | 'error';
};
export interface NativeProps extends ViewProps {
sourceURL?: string;
onScriptLoaded?: CodegenTypes.BubblingEventHandler<WebViewScriptLoadedEvent> | null;
}
export default codegenNativeComponent<NativeProps>(
'CustomWebView',
) as HostComponent<NativeProps>;
这里主要有三部分:
-
WebViewScriptLoadedEvent是一种支持的数据类型,用于存储事件需要从原生代码传递到 JavaScript 的数据。 -
NativeProps定义了可以设置的组件属性。 -
codegenNativeComponent允许我们为自定义组件生成代码,并为组件定义一个与原生实现相匹配的名称。
其中,对codegenNativeComponent函数进行分析,源码react-native/packages/react-native/Libraries/Utilities/codegenNativeComponent.js:
js
// 如果这个函数运行,说明视图配置没有在构建时通过 `GenerateViewConfigJs.js` 生成。
// 因此我们需要使用 `requireNativeComponent` 从视图管理器获取视图配置。
// `requireNativeComponent` 在 Bridgeless 模式下不可用。
// 例如:如果 `codegenNativeComponent` 不是从以 NativeComponent.js 结尾的文件中调用,
// 这个函数就会在运行时执行。
function codegenNativeComponent<Props: {...}>(
componentName: string,
options?: NativeComponentOptions,
): NativeComponentType<Props> {
if (global.RN$Bridgeless === true && __DEV__) {
console.warn(
`Codegen didn't run for ${componentName}. This will be an error in the future. Make sure you are using @react-native/babel-preset when building your JavaScript code.`,
);
}
// 确定基础组件名称
let componentNameInUse =
options && options.paperComponentName != null
? options.paperComponentName
: componentName;
// 省略部分代码......
return (requireNativeComponent<Props>(
// $FlowFixMe[incompatible-type]
componentNameInUse,
): HostComponent<Props>);
}
根据注释可知,在新架构中,这个函数几乎没有实际意义,因为新架构是通过GenerateViewConfigJs.js 来生成实际的视图配置。
我们通过搜索codegenNativeComponent字符串,很容易定位到react-native/packages/babel-plugin-codegen/index.js文件,这里babel-plugin-codegen 包就是检测codegenNativeComponent调用,解析TypeScript/Flow类型定义的工具包:
js
module.exports = function ({parse, types: t}) {
return {
pre(state) {
this.code = state.code;
this.filename = state.opts.filename;
this.defaultExport = null;
this.commandsExport = null;
this.codeInserted = false;
},
// 省略部分代码.....
ExportDefaultDeclaration(path, state) {
if (isCodegenDeclaration(path.node.declaration)) {
this.defaultExport = path;
}
},
// 程序退出时进行替换
Program: {
exit(path) {
if (this.defaultExport) {
// 1. 生成ViewConfig代码
const viewConfig = generateViewConfig(this.filename, this.code);
// 2. 解析为AST
const ast = parse(viewConfig, {
babelrc: false,
browserslistConfigFile: false,
configFile: false,
});
// 3. 完全替换原始导出
this.defaultExport.replaceWithMultiple(ast.program.body);
if (this.commandsExport != null) {
this.commandsExport.remove();
}
this.codeInserted = true;
}
},
},
},
};
};
function generateViewConfig(filename /*: string */, code /*: string */) {
// 解析TypeScript/Flow类型
const schema = parseFile(filename, code);
// 提取组件信息
const libraryName = basename(filename).replace(
/NativeComponent\.(js|ts)$/,
'',
);
// 调用Codegen生成器
return RNCodegen.generateViewConfig({
libraryName,
schema,
});
}
function isCodegenDeclaration(declaration) {
if (!declaration) {
return false;
}
if (
declaration.left &&
declaration.left.left &&
declaration.left.left.name === 'codegenNativeComponent'
) {
return true;
} else if (
declaration.callee &&
declaration.callee.name &&
declaration.callee.name === 'codegenNativeComponent'
) {
return true;
}
// 省略......
return false;
}
继续跟踪RNCodegen.generateViewConfig的实现,源码react-native/packages/react-native-codegen/src/generators/RNCodegen.js:
js
const generateViewConfigJs = require('./components/GenerateViewConfigJs.js');
module.exports = {
allGenerators: ALL_GENERATORS,
// 省略部分代码......
generateViewConfig({
libraryName,
schema,
}: Pick<LibraryOptions, 'libraryName' | 'schema'>): string {
schemaValidator.validate(schema);
const result = generateViewConfigJs
.generate(libraryName, schema)
.values()
.next();
if (typeof result.value !== 'string') {
throw new Error(`Failed to generate view config for ${libraryName}`);
}
return result.value;
},
};
最终是调用的GenerateViewConfigJs.js中的generate,源码react-native/packages/react-native-codegen/src/generators/components/GenerateViewConfigJs.js:
js
module.exports = {
generate(libraryName: string, schema: SchemaType): FilesOutput {
try {
const fileName = `${libraryName}NativeViewConfig.js`;
const imports: Set<string> = new Set();
const moduleResults = Object.keys(schema.modules)
.map(moduleName => {
const module = schema.modules[moduleName];
if (module.type !== 'Component') {
return;
}
const {components} = module;
return Object.keys(components)
.map((componentName: string) => {
const component = components[componentName];
if (component.paperComponentNameDeprecated) {
imports.add(UIMANAGER_IMPORT);
}
const replacedTemplate = ComponentTemplate({
componentName,
paperComponentName: component.paperComponentName,
paperComponentNameDeprecated:
component.paperComponentNameDeprecated,
});
// 省略部分代码......
return new Map([[fileName, replacedTemplate]]);
} catch (error) {
console.error(`\nError parsing schema for ${libraryName}\n`);
console.error(JSON.stringify(schema));
throw error;
}
},
};
这里我们根据ComponentTemplate中的模板,大概就能还原出生成的代码是什么样子:
js
// 输入:
// libraryName: "MyComponent"
// schema: { 解析后的TypeScript/Flow类型信息 }
// 输出:完整的JavaScript代码字符串
'use strict';
const {UIManager} = require("react-native")
let nativeComponentName = 'MyComponent';
export const __INTERNAL_VIEW_CONFIG = {
uiViewClassName: 'MyComponent',
bubblingEventTypes: {},
directEventTypes: {},
validAttributes: {
opacity: true,
backgroundColor: { process: require('react-native/Libraries/StyleSheet/processColor').default },
// ... 其他属性
}
};
export default NativeComponentRegistry.get(nativeComponentName, () => __INTERNAL_VIEW_CONFIG);
这里最关键的就是最后一行,它将开发者编写的Fabric 组件规范进行了代码替换:
js
// 开发者编写
const CustomView = codegenNativeComponent<Props>('CustomView');
// Babel插件替换后
const CustomView = NativeComponentRegistry.get('CustomView', () => __INTERNAL_VIEW_CONFIG);
需要注意,babel-plugin-codegen 工具并不像Codegen工具,会生成实际的代码文件。它是对AST语法树进行的动态修改和替换,也就是说它修改的是内存中的语法树,并不会写文件。
现在来重点追踪NativeComponentRegistry.get的实现,首先是导出位置react-native/packages/react-native/index.js:
js
get NativeComponentRegistry() {
return require('./Libraries/NativeComponent/NativeComponentRegistry');
},
定位到方法实现react-native/packages/react-native/Libraries/NativeComponent/NativeComponentRegistry.js:
js
/**
* 获取一个可以被 React Native 渲染的 `NativeComponent`。
*
* 提供的 `viewConfigProvider` 可能会被调用和使用,也可能不会,
* 这取决于 `setRuntimeConfigProvider` 是如何配置的。
*/
export function get<Config: {...}>(
name: string,
viewConfigProvider: () => PartialViewConfig,
): HostComponent<Config> {
// 注册ViewConfig到全局注册表
ReactNativeViewConfigRegistry.register(name, () => {
// 这里的回调函数会在React需要组件配置时被调用
const {native, verify} = getRuntimeConfig?.(name) ?? {
native: !global.RN$Bridgeless, // 关键:新架构检测
verify: false,
};
let viewConfig: ViewConfig;
if (native) {
// 旧架构:原生ViewManager
viewConfig =
getNativeComponentAttributes(name) ??
createViewConfig(viewConfigProvider());
} else {
// 新架构:优先静态ViewConfig
viewConfig =
createViewConfig(viewConfigProvider()) ??
getNativeComponentAttributes(name);
}
// 省略部分代码......
return viewConfig;
});
// $FlowFixMe[incompatible-type] `NativeComponent` 实际上是字符串!
return name;
}
继续跟踪ReactNativeViewConfigRegistry.register实现。源码react-native/packages/react-native/Libraries/Renderer/shims/ReactNativeViewConfigRegistry.js:
js
const viewConfigCallbacks = new Map<string, ?() => ViewConfig>();
export function register(name: string, callback: () => ViewConfig): string {
// 省略部分代码......
viewConfigCallbacks.set(name, callback);
return name;
}
这里基本上就是将返回ViewConfig的闭包给存了起来。
查找组件
JS层
在前面启动渲染一节我们知道了启动渲染的最终调用是JS层的AppRegistry.runApplication方法。沿着这条线,我们来分析一下JS层的组件加载与处理流程。源码react-native/packages/react-native/Libraries/ReactNative/AppRegistry.js:
js
import * as AppRegistry from './AppRegistryImpl';
// 省略部分代码......
global.RN$AppRegistry = AppRegistry;
registerCallableModule('AppRegistry', AppRegistry);
export {AppRegistry};
继续跟踪源码react-native/packages/react-native/Libraries/ReactNative/AppRegistryImpl.js:
js
const runnables: Runnables = {};
/**
* Loads the JavaScript bundle and runs the app.
*
* See https://reactnative.dev/docs/appregistry#runapplication
*/
export function runApplication(
appKey: string,
appParameters: AppParameters,
displayMode?: number,
): void {
if (appKey !== 'LogBox') {
const logParams = __DEV__ ? ` with ${JSON.stringify(appParameters)}` : '';
const msg = `Running "${appKey}"${logParams}`;
console.log(msg);
}
SceneTracker.setActiveScene({name: appKey});
runnables[appKey](appParameters, coerceDisplayMode(displayMode));
}
/**
* Registers an app's root component.
*
* See https://reactnative.dev/docs/appregistry#registercomponent
*/
export function registerComponent(
appKey: string,
componentProvider: ComponentProvider,
section?: boolean,
): string {
const scopedPerformanceLogger = createPerformanceLogger();
runnables[appKey] = (appParameters, displayMode) => {
const renderApplication = require('./renderApplication').default;
renderApplication(
componentProviderInstrumentationHook(
componentProvider,
scopedPerformanceLogger,
),
appParameters.initialProps,
appParameters.rootTag,
wrapperComponentProvider && wrapperComponentProvider(appParameters),
rootViewStyleProvider && rootViewStyleProvider(appParameters),
appParameters.fabric,
scopedPerformanceLogger,
appKey === 'LogBox', // is logbox
appKey,
displayMode,
);
};
if (section) {
sections[appKey] = runnables[appKey];
}
return appKey;
}
可以看到,runApplication调用的是runnables对象中注册的闭包。而在我们React Native JS层的Bundle包中,首先就需要调用AppRegistry.registerComponent(appName, () => App)完成最根组件的注册。所以runApplication方法中调用的闭包,就是在registerComponent中注册的闭包。
继续跟踪renderApplication方法的实现,源码react-native/packages/react-native/Libraries/ReactNative/renderApplication.js:
js
export default function renderApplication<Props: Object>(
RootComponent: React.ComponentType<Props>,
initialProps: Props,
rootTag: any,
WrapperComponent?: ?React.ComponentType<any>,
rootViewStyle?: ?ViewStyleProp,
fabric?: boolean,
scopedPerformanceLogger?: IPerformanceLogger,
isLogBox?: boolean,
debugName?: string,
displayMode?: ?DisplayModeType,
useOffscreen?: boolean,
) {
const performanceLogger = scopedPerformanceLogger ?? GlobalPerformanceLogger;
// 构建React元素树
// 外层:PerformanceLoggerContext.Provider - 提供性能监控上下文
// 中层:AppContainer - React Native的根容器组件
// 内层:RootComponent - 用户注册的应用组件(如App.js)
let renderable: React.MixedElement = (
<PerformanceLoggerContext.Provider value={performanceLogger}>
<AppContainer
rootTag={rootTag}
fabric={fabric}
WrapperComponent={WrapperComponent}
rootViewStyle={rootViewStyle}
initialProps={initialProps ?? Object.freeze({})}
internal_excludeLogBox={isLogBox}>
<RootComponent {...initialProps} rootTag={rootTag} />
</AppContainer>
</PerformanceLoggerContext.Provider>
);
// 开发模式调试包装
if (__DEV__ && debugName) {
const RootComponentWithMeaningfulName = getCachedComponentWithDebugName(
`${debugName}(RootComponent)`,
);
renderable = (
<RootComponentWithMeaningfulName>
{renderable}
</RootComponentWithMeaningfulName>
);
}
// 实验性离屏渲染支持
if (useOffscreen && displayMode != null) {
// $FlowFixMe[incompatible-type]
// $FlowFixMe[prop-missing]
// $FlowFixMe[missing-export]
const Activity: ActivityType = React.unstable_Activity;
renderable = (
<Activity
mode={displayMode === DisplayMode.VISIBLE ? 'visible' : 'hidden'}>
{renderable}
</Activity>
);
}
// 我们希望在使用 Fabric 时始终启用 concurrentRoot 功能
const useConcurrentRoot = Boolean(fabric);
// 省略部分性能日志打印......
// 进入React渲染系统
Renderer.renderElement({
element: renderable,
rootTag,
useFabric: Boolean(fabric),
useConcurrentRoot,
});
}
继续跟踪Renderer.renderElement方法实现。源码react-native/packages/react-native/Libraries/ReactNative/RendererImplementation.js:
js
export function renderElement({
element,
rootTag,
useFabric,
useConcurrentRoot,
}: {
element: React.MixedElement,
rootTag: number,
useFabric: boolean,
useConcurrentRoot: boolean,
}): void {
if (useFabric) {
if (cachedFabricRender == null) {
// 获取实际渲染器
cachedFabricRender = getFabricRenderer().render;
}
cachedFabricRender(element, rootTag, null, useConcurrentRoot, {
onCaughtError,
onUncaughtError,
onRecoverableError,
});
} else {
// 省略旧架构......
}
}
function getFabricRenderer(): ReactFabricType {
if (cachedFabricRenderer == null) {
cachedFabricRenderer = require('../Renderer/shims/ReactFabric').default;
}
return cachedFabricRenderer;
}
继续跟踪react-native/packages/react-native/Libraries/Renderer/shims/ReactFabric.js:
js
import {BatchedBridge} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
import type {ReactFabricType} from './ReactNativeTypes';
let ReactFabric: ReactFabricType;
if (__DEV__) {
ReactFabric = require('../implementations/ReactFabric-dev');
} else {
ReactFabric = require('../implementations/ReactFabric-prod');
}
global.RN$stopSurface = ReactFabric.stopSurface;
if (global.RN$Bridgeless !== true) {
BatchedBridge.registerCallableModule('ReactFabric', ReactFabric);
}
export default ReactFabric;
这里根据开发环境选择了不同的渲染器。开发环境的渲染器体积更大,包含了许多调试、性能日志等信息,而生产环境的移除了注释和空白,变量名被压缩代码经过优化,减少包体积。
继续跟踪生产环境的渲染器,因为代码更加简洁,可更好的聚焦于调用的链路和流程。源码react-native/packages/react-native/Libraries/Renderer/implementations/ReactFabric-prod.js:
js
exports.render = function (
element,
containerTag,
callback,
concurrentRoot,
options
) {
var root = roots.get(containerTag);
if (!root) {
// 创建新的FiberRootNode
// 省略部分代码......
initializeUpdateQueue(concurrentRoot);
roots.set(containerTag, root);
}
// 启动渲染
updateContainer(element, root, null, callback);
// 返回渲染后的公共实例引用
a: if (((element = root.current), element.child))
switch (element.child.tag) {
case 27:
case 5:
element = getPublicInstance(element.child.stateNode);
break a;
default:
element = element.child.stateNode;
}
else element = null;
return element;
};
function updateContainer(element, container, parentComponent, callback) {
parentComponent = container.current;
// / 获取更新优先级
var lane = requestUpdateLane(parentComponent);
null === container.context
? (container.context = emptyContextObject)
: (container.pendingContext = emptyContextObject);
// 创建更新对象
container = createUpdate(lane);
container.payload = { element: element };
callback = void 0 === callback ? null : callback;
null !== callback && (container.callback = callback);
// 将更新加入队列
element = enqueueUpdate(parentComponent, container, lane);
// 调度更新
null !== element &&
(scheduleUpdateOnFiber(element, parentComponent, lane),
entangleTransitions(element, parentComponent, lane));
return lane;
}
以上调用都比较简单,这里的核心是scheduleUpdateOnFiber方法,它负责调度更新。由于代码量较大,我们后面只追求核心逻辑,将省略大部分代码:
js
function scheduleUpdateOnFiber(root, fiber, lane) {
// 省略......
// 标记更新并触发调度
markRootUpdated$1(root, lane);
if (/* 不在渲染中 */) {
ensureRootIsScheduled(root); // 确保根节点被调度
if (/* 同步更新 */) {
flushSyncWorkAcrossRoots_impl(0, !0); // 立即执行同步工作
}
}
}
function flushSyncWorkAcrossRoots_impl(syncTransitionLanes, onlyLegacy) {
if (!isFlushingWork && mightHavePendingSyncWork) {
isFlushingWork = !0;
do {
var didPerformSomeWork = !1;
// 遍历所有根节点执行同步工作
for (var root = firstScheduledRoot; null !== root; ) {
if (!onlyLegacy || 0 === root.tag)
//省略......
// 常规同步更新
performSyncWorkOnRoot(root, JSCompiler_inline_result));
root = root.next;
}
} while (didPerformSomeWork);
isFlushingWork = !1;
}
}
function performSyncWorkOnRoot(root, lanes) {
if (flushPendingEffects()) return null;
performWorkOnRoot(root, lanes, !0);
}
function performWorkOnRoot(root$jscomp$0, lanes, forceSync) {
// 选择同步或并发渲染
var shouldTimeSlice = (!forceSync && /* 条件判断 */);
var exitStatus = shouldTimeSlice
? renderRootConcurrent(root, lanes) // 并发渲染
: renderRootSync(root, lanes, !0); // 同步渲染
// 省略......
}
继续追踪同步渲染renderRootSync方法实现:
js
function renderRootSync(root, lanes, shouldYieldForPrerendering) {
// 省略......
// 同步工作循环
workLoopSync();
// 省略......
}
function workLoopSync() {
for (; null !== workInProgress; ) performUnitOfWork(workInProgress);
}
function performUnitOfWork(unitOfWork) {
// 处理单个Fiber节点
var next = beginWork(unitOfWork.alternate, unitOfWork, entangledRenderLanes);
unitOfWork.memoizedProps = unitOfWork.pendingProps;
null === next ? completeUnitOfWork(unitOfWork) : (workInProgress = next);
}
function completeUnitOfWork(unitOfWork) {
var completedWork = unitOfWork;
do {
// 省略......
unitOfWork = completedWork.return;
var next = completeWork(
completedWork.alternate,
completedWork,
entangledRenderLanes
);
if (null !== next) {
workInProgress = next;
return;
}
completedWork = completedWork.sibling;
if (null !== completedWork) {
workInProgress = completedWork;
return;
}
workInProgress = completedWork = unitOfWork;
} while (null !== completedWork);
0 === workInProgressRootExitStatus && (workInProgressRootExitStatus = 5);
}
最终是调用的completeWork方法完成渲染工作:
js
function completeWork(current, workInProgress, renderLanes) {
var newProps = workInProgress.pendingProps;
switch (workInProgress.tag) {
case 28:
case 16:
case 15:
case 0:
case 11:
case 7:
case 8:
case 12:
case 9:
case 14:
return bubbleProperties(workInProgress), null;
case 1:
return bubbleProperties(workInProgress), null;
case 3:
// 省略......
// return
case 26:
case 27:
case 5:
popHostContext(workInProgress);
var type = workInProgress.type;
if (null !== current && null != workInProgress.stateNode)
// 省略......
else {
// 首次挂载
if (!newProps) {
if (null === workInProgress.stateNode)
throw Error(
"We must have new props for new mounts. This error is likely caused by a bug in React. Please file an issue."
);
bubbleProperties(workInProgress);
return null;
}
current = rootInstanceStackCursor.current;
renderLanes = nextReactTag;
nextReactTag += 2;
// 获取ViewConfig
type = getViewConfigForType(type);
var updatePayload = ReactNativePrivateInterface.createAttributePayload(
newProps,
type.validAttributes
);
// 创建原生节点
current = {
node: createNode(
renderLanes,
type.uiViewClassName, // 使用ViewConfig中的原生类名
current.containerTag,
updatePayload,
workInProgress
),
canonical: {
nativeTag: renderLanes,
viewConfig: type, // 保存ViewConfig引用
currentProps: newProps,
internalInstanceHandle: workInProgress,
publicInstance: null,
publicRootInstance: current.publicInstance
}
};
workInProgress.flags |= 8;
appendAllChildren(current, workInProgress, !1, !1);
workInProgress.stateNode = current;
}
bubbleProperties(workInProgress);
workInProgress.flags &= -16777217;
return null;
// 省略部分代码......
}
}
此方法中的case都是整数,为了弄清楚其含义,我们可以查看React 仓库中的Tag 定义。源码位于 ReactWorkTags.js:
js
export const FunctionComponent = 0;
export const ClassComponent = 1;
export const HostRoot = 3; // 宿主树的根节点。它可以嵌套在另一个节点内部
export const HostPortal = 4; // 一个子树。它可以是通往不同渲染器的入口点
export const HostComponent = 5;
// 省略......
很显然,我们需要查看的是React Native的Tag类型,也就是HostComponent,它的值是5,因此对应到completeWork中的处理代码就是我截取的这部分。这段代码中有一个关键的方法,就是getViewConfigForType,查看其定义,显示为getViewConfigForType = ReactNativePrivateInterface.ReactNativeViewConfigRegistry.get
可看到,这里就完全与前面声明组件 一节最后分析到的ReactNativeViewConfigRegistry.js对应上了,我们前面分析的是ViewConfig的注册,现在来看一下get方法:
js
/**
* 获取指定视图的配置。如果这是第一次使用该视图,此配置将从UIManager延迟加载
*/
export function get(name: string): ViewConfig {
// 从viewConfigs Map中查找已缓存的ViewConfig
let viewConfig = viewConfigs.get(name);
if (viewConfig == null) {
// 获取该组件的配置生成回调函数
const callback = viewConfigCallbacks.get(name);
if (typeof callback !== 'function') {
// 省略日志......
}
viewConfig = callback();
invariant(viewConfig, 'View config not found for component `%s`', name);
processEventTypes(viewConfig);
viewConfigs.set(name, viewConfig);
// 配置设置后清除回调,这样我们就不会在注册过程中掩盖任何错误。
viewConfigCallbacks.set(name, null);
}
return viewConfig;
}
通过ViewConfig ,可以得到uiViewClassName,也就是声明的组件名称,我们继续查看原生节点创建的方法:
js
var _nativeFabricUIManage = nativeFabricUIManager,
createNode = _nativeFabricUIManage.createNode,
createNode(
renderLanes,
type.uiViewClassName, // 使用ViewConfig中的原生类名
current.containerTag,
updatePayload,
workInProgress
)
createNode方法来自于全局对象nativeFabricUIManager,通过变量名就知道,这个应该是来自于JSI定义的对象,代码不在JS层。
C++ 层
继续在C++中搜索nativeFabricUIManager,定位到源码react-native/packages/react-native/ReactCommon/react/renderer/uimanager/UIManagerBinding.cpp:
js
void UIManagerBinding::createAndInstallIfNeeded(
jsi::Runtime& runtime,
const std::shared_ptr<UIManager>& uiManager) {
auto uiManagerModuleName = "nativeFabricUIManager";
auto uiManagerValue =
runtime.global().getProperty(runtime, uiManagerModuleName);
if (uiManagerValue.isUndefined()) {
// 全局命名空间中没有该绑定的实例;我们需要创建、安装并返回它
auto uiManagerBinding = std::make_shared<UIManagerBinding>(uiManager);
auto object = jsi::Object::createFromHostObject(runtime, uiManagerBinding);
runtime.global().setProperty(
runtime, uiManagerModuleName, std::move(object));
}
}
可见这里的nativeFabricUIManager是一个jsi::HostObject对象,我们要查找其createNode方法,直接查看UIManagerBinding 的get方法:
cpp
jsi::Value UIManagerBinding::get(
jsi::Runtime& runtime,
const jsi::PropNameID& name) {
auto methodName = name.utf8(runtime);
// 将 shared_ptr<UIManager> 转换为原始指针
// 为什么这样做?原因如下:
// 1) UIManagerBinding 强引用(strongly retains)UIManager。
// JS VM 通过 JSI 强引用 UIManagerBinding。
// 这些函数是 JSI 函数,只能通过 JS VM 调用;如果 JS VM 被销毁,
// 这些函数无法执行,这些 lambda 也不会执行。
// 2) UIManager 只有在所有对它的引用都被释放后才会被析构,包括
// UIManagerBinding。这只有在 JS VM 被析构时才会发生。因此,原始指针是安全的。
//
// 即使这样做是安全的,为什么不直接使用 shared_ptr 作为额外的保险呢?
// 1) 在不需要的情况下使用 shared_ptr 或 weak_ptr
// 是一种性能劣化(pessimisation)。
// 在这种情况下,它会执行更多指令,但不会带来任何额外价值。
// 2) 这些 lambda 的确切释放时机和方式很复杂。向它们添加 shared_ptr 会导致
// UIManager 可能存活更长时间,这是不必要的、复杂的认知负担。
// 3) 有强烈怀疑认为,从这些 C++ lambda 中保留 UIManager(这些 lambda 被
// JSI 持有的对象所保留),在 Scheduler 和 JS VM 析构时导致了一些崩溃。
// 如果 C++ 语义导致这些 lambda 在 JS VM 被析构后一个 CPU
// 时钟周期(或更久) 才被释放,就可能发生这种情况。
UIManager* uiManager = uiManager_.get();
// Semantic: Creates a new node with given pieces.
if (methodName == "createNode") {
auto paramCount = 5;
return jsi::Function::createFromHostFunction(
runtime,
name,
paramCount,
[uiManager, methodName, paramCount](
jsi::Runtime& runtime,
const jsi::Value& /*thisValue*/,
const jsi::Value* arguments,
size_t count) -> jsi::Value {
try {
validateArgumentCount(runtime, methodName, paramCount, count);
auto instanceHandle =
instanceHandleFromValue(runtime, arguments[4], arguments[0]);
if (!instanceHandle) {
react_native_assert(false);
return jsi::Value::undefined();
}
return valueFromShadowNode(
runtime,
uiManager->createNode(
tagFromValue(arguments[0]),
stringFromValue(runtime, arguments[1]),
surfaceIdFromValue(runtime, arguments[2]),
RawProps(runtime, arguments[3]),
std::move(instanceHandle)),
true);
} catch (const std::logic_error& ex) {
LOG(FATAL) << "logic_error in createNode: " << ex.what();
}
});
}
// 省略部分代码......
return jsi::Value::undefined();
}
继续跟踪react-native/packages/react-native/ReactCommon/react/renderer/uimanager/UIManager.cpp中的createNode实现:
cpp
std::shared_ptr<ShadowNode> UIManager::createNode(
Tag tag, // 节点标签
const std::string& name, // 组件名称
SurfaceId surfaceId,
RawProps rawProps, // 原始属性
InstanceHandle::Shared instanceHandle) const {
TraceSection s("UIManager::createNode", "componentName", name);
// 根据组件名称获取对应的ComponentDescriptor
auto& componentDescriptor = componentDescriptorRegistry_->at(name);
auto fallbackDescriptor =
componentDescriptorRegistry_->getFallbackComponentDescriptor();
PropsParserContext propsParserContext{surfaceId, *contextContainer_};
// 创建ShadowNodeFamily,用于管理同一组件的不同实例
auto family = componentDescriptor.createFamily(
{.tag = tag,
.surfaceId = surfaceId,
.instanceHandle = std::move(instanceHandle)});
// 解析和克隆属性
const auto props = componentDescriptor.cloneProps(
propsParserContext, nullptr, std::move(rawProps));
// 创建初始状态
const auto state = componentDescriptor.createInitialState(props, family);
// 创建ShadowNode
auto shadowNode = componentDescriptor.createShadowNode(
ShadowNodeFragment{
.props = fallbackDescriptor != nullptr &&
fallbackDescriptor->getComponentHandle() ==
componentDescriptor.getComponentHandle()
? componentDescriptor.cloneProps(
propsParserContext,
props,
RawProps(folly::dynamic::object("name", name)))
: props,
.children = ShadowNodeFragment::childrenPlaceholder(),
.state = state,
},
family);
if (delegate_ != nullptr) {
delegate_->uiManagerDidCreateShadowNode(*shadowNode);
}
if (leakChecker_) {
leakChecker_->uiManagerDidCreateShadowNodeFamily(family);
}
return shadowNode;
}
可以看到,这里通过componentDescriptorRegistry_来查找Fabric 组件描述对象,至于注册的地方,下一节注册组件会专门分析。
这里还有一点要注意,createShadowNode方法只是创建了虚拟的ShadowNode,并没有创建真正的原生视图。ShadowNode是Fabric中的虚拟DOM节点,用于布局计算。也就是说当完成布局和diff计算后,会生成MountItem指令。
Kotlin层
在前面启动渲染 一节中,我们有分析到createViewUnsafe方法,回顾一下安卓原生组件的查找:
kotlin
ViewManager viewManager = mViewManagerRegistry.get(componentName);
跟踪源码react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerRegistry.kt:
kotlin
@Synchronized
public fun get(className: String): ViewManager<*, *> {
// 1. Try to get the manager without the prefix.
viewManagersMap[className]?.let {
return it
}
// 2. Try to get the manager with the RCT prefix.
val rctViewManagerName = "RCT$className"
viewManagersMap[rctViewManagerName]?.let {
return it
}
if (viewManagerResolver != null) {
// 1. Try to get the manager without the prefix.
val resolvedManager = getViewManagerFromResolver(className)
if (resolvedManager != null) {
return resolvedManager
}
// 2. Try to get the manager with the RCT prefix.
val rctResolvedManager = getViewManagerFromResolver(rctViewManagerName)
if (rctResolvedManager != null) {
return rctResolvedManager
}
throw IllegalViewOperationException(
"Can't find ViewManager '$className' nor '$rctViewManagerName' in ViewManagerRegistry, " +
"existing names are: ${viewManagerResolver.getViewManagerNames()}"
)
}
throw IllegalViewOperationException("No ViewManager found for class $className")
}
private fun getViewManagerFromResolver(className: String): ViewManager<*, *>? {
val viewManager = viewManagerResolver?.getViewManager(className)
if (viewManager != null) {
viewManagersMap[className] = viewManager
}
return viewManager
}
新架构的情况下,是通过getViewManagerFromResolver方法来查找,其中viewManagerResolver的类型是BridgelessViewManagerResolver,它是一个内部类,定义在ReactInstance.kt文件中:
kotlin
private class BridgelessViewManagerResolver(
private val reactPackages: List<ReactPackage>,
private val context: BridgelessReactContext,
) : ViewManagerResolver {
private val lazyViewManagerMap: MutableMap<String, ViewManager<*, *>> = HashMap()
override fun getViewManager(viewManagerName: String): ViewManager<*, *>? {
// 从懒加载包中查找
val viewManager = getLazyViewManager(viewManagerName)
if (viewManager != null) {
return viewManager
}
// 如果通过延迟加载在所有 React 包中都找不到视图管理器,则回退到默认实现:立即初始化所有视图管理器
return eagerViewManagerMap[viewManagerName]
}
private lateinit var _eagerViewManagerMap: Map<String, ViewManager<*, *>>
@get:Synchronized
val eagerViewManagerMap: Map<String, ViewManager<*, *>>
get() {
if (::_eagerViewManagerMap.isInitialized) {
return _eagerViewManagerMap
}
val viewManagerMap: MutableMap<String, ViewManager<*, *>> = HashMap()
for (reactPackage in reactPackages) {
if (reactPackage is ViewManagerOnDemandReactPackage) {
continue
}
val viewManagersInPackage = reactPackage.createViewManagers(context)
for (viewManager in viewManagersInPackage) {
// TODO(T173624687): Should we throw/warn when the same view manager name is registered
// twice?
viewManagerMap[viewManager.name] = viewManager
}
}
_eagerViewManagerMap = viewManagerMap
return viewManagerMap
}
@Synchronized
fun getLazyViewManager(viewManagerName: String): ViewManager<*, *>? {
// 先查缓存
if (lazyViewManagerMap.containsKey(viewManagerName)) {
return lazyViewManagerMap[viewManagerName]
}
// 缓存未命中则遍历所有 reactPackages,调用 createViewManager(name) 创建
for (reactPackage in reactPackages) {
if (reactPackage is ViewManagerOnDemandReactPackage) {
val viewManager = reactPackage.createViewManager(context, viewManagerName)
if (viewManager != null) {
// TODO(T173624687): Should we throw/warn when the same view manager name is registered
// twice?
lazyViewManagerMap[viewManagerName] = viewManager
return viewManager
}
}
}
return null
}
// 省略部分代码......
}
注册组件
JS工具
React Native新架构使用了很多代码生成工具,以致于成了黑箱操作,这对于Turbo Module和Fabric组件的注册流程造成了理解上的困难。为此,我们不得不研究这些CLI工具。首先研究@react-native-community/cli工具,源码 cli。这里我们使用的版本是v20.0.2。
查看源码cli/packages/cli-config-android/src/config/index.ts:
ts
export function dependencyConfig(
root: string,
userConfig: AndroidDependencyParams | null = {},
): AndroidDependencyConfig | null {
if (userConfig === null) {
return null;
}
const src = userConfig.sourceDir || findAndroidDir(root);
if (!src) {
return null;
}
const sourceDir = path.join(root, src);
const manifestPath = userConfig.manifestPath
? path.join(sourceDir, userConfig.manifestPath)
: findManifest(sourceDir);
const buildGradlePath = findBuildGradle(sourceDir, '');
const isPureCxxDependency =
userConfig.cxxModuleCMakeListsModuleName != null &&
userConfig.cxxModuleCMakeListsPath != null &&
userConfig.cxxModuleHeaderName != null &&
!manifestPath &&
!buildGradlePath;
if (!manifestPath && !buildGradlePath && !isPureCxxDependency) {
return null;
}
let packageImportPath = null,
packageInstance = null;
if (!isPureCxxDependency) {
const packageName =
userConfig.packageName || getPackageName(manifestPath, buildGradlePath);
const packageClassName = findPackageClassName(sourceDir);
/**
* This module has no package to export
*/
if (!packageClassName) {
return null;
}
packageImportPath =
userConfig.packageImportPath ||
`import ${packageName}.${packageClassName};`;
packageInstance = userConfig.packageInstance || `new ${packageClassName}()`;
}
const buildTypes = userConfig.buildTypes || [];
const dependencyConfiguration = userConfig.dependencyConfiguration;
const libraryName =
userConfig.libraryName || findLibraryName(root, sourceDir);
const componentDescriptors =
userConfig.componentDescriptors || findComponentDescriptors(root);
let cmakeListsPath = userConfig.cmakeListsPath
? path.join(sourceDir, userConfig.cmakeListsPath)
: path.join(sourceDir, 'build/generated/source/codegen/jni/CMakeLists.txt');
const cxxModuleCMakeListsModuleName =
userConfig.cxxModuleCMakeListsModuleName || null;
const cxxModuleHeaderName = userConfig.cxxModuleHeaderName || null;
let cxxModuleCMakeListsPath = userConfig.cxxModuleCMakeListsPath
? path.join(sourceDir, userConfig.cxxModuleCMakeListsPath)
: null;
if (process.platform === 'win32') {
cmakeListsPath = cmakeListsPath.replace(/\\/g, '/');
if (cxxModuleCMakeListsPath) {
cxxModuleCMakeListsPath = cxxModuleCMakeListsPath.replace(/\\/g, '/');
}
}
return {
sourceDir,
packageImportPath,
packageInstance,
buildTypes,
dependencyConfiguration,
libraryName,
componentDescriptors,
cmakeListsPath,
cxxModuleCMakeListsModuleName,
cxxModuleCMakeListsPath,
cxxModuleHeaderName,
isPureCxxDependency,
};
}
此dependencyConfig函数非常重要,它主要用于生成example/android/build/generated/autolinking/autolinking.json文件。autolinking.json文件我们在前面的TurboModule的文章已经介绍过了,其中的信息是指导代码生成的关键。这里重点关注componentDescriptors字段的生成,因为它是Fabric组件代码生成的起始。
组件开发者通常并不会在react-native.config.js文件中主动配置componentDescriptors字段,一般都是默认值,那么就要查看一下findComponentDescriptors函数的实现逻辑了,源码cli/packages/cli-config-android/src/config/findComponentDescriptors.ts:
ts
export function findComponentDescriptors(packageRoot: string) {
let jsSrcsDir = null;
try {
const packageJson = fs.readFileSync(
path.join(packageRoot, 'package.json'),
'utf8',
);
jsSrcsDir = JSON.parse(packageJson).codegenConfig.jsSrcsDir;
} catch (error) {
// no jsSrcsDir, continue with default glob pattern
}
const globPattern = jsSrcsDir
? `${jsSrcsDir}/**/*{.js,.jsx,.ts,.tsx}`
: '**/*{.js,.jsx,.ts,.tsx}';
const files = glob.sync(globPattern, {
cwd: unixifyPaths(packageRoot),
onlyFiles: true,
ignore: ['**/node_modules/**'],
});
const codegenComponent = files
.map((filePath) =>
fs.readFileSync(path.join(packageRoot, filePath), 'utf8'),
)
.map(extractComponentDescriptors)
.filter(Boolean);
// Filter out duplicates as it happens that libraries contain multiple outputs due to package publishing.
// TODO: consider using "codegenConfig" to avoid this.
return Array.from(new Set(codegenComponent as string[]));
}
这里主要逻辑其实是从package.json中获取到jsSrcsDir配置,从而定位到源码所在目录。我们关注的是codegenComponent的生成,此处是通过读取每个源文件内容,然后调用 extractComponentDescriptors 提取组件描述符。继续查看cli/packages/cli-config-android/src/config/extractComponentDescriptors.ts:
ts
const CODEGEN_NATIVE_COMPONENT_REGEX =
/codegenNativeComponent(<.*>)?\s*\(\s*["'`](\w+)["'`](,?[\s\S]+interfaceOnly:\s*(\w+))?/m;
export function extractComponentDescriptors(contents: string) {
const match = contents.match(CODEGEN_NATIVE_COMPONENT_REGEX);
if (!(match?.[4] === 'true') && match?.[2]) {
return `${match[2]}ComponentDescriptor`;
}
return null;
}
到这里就很清楚了,扫描所有源码,使用正则找到我们在声明组件一节中提到的export default codegenNativeComponent<NativeProps>('CustomviewView');这行代码,这里的正则就是匹配关键字codegenNativeComponent。这里的match?.[2]就是提前出组件名,也就是我们示例中的CustomviewView。最终返回的字符串是拼接后的,这里就是CustomviewViewComponentDescriptor。也就是说componentDescriptors字段的默认值是CustomviewViewComponentDescriptor。
接下来我们回顾前文《Android端TurboModule分析》,其中提到但略过的React Gradle脚本的configureCodegen方法:
kotlin
private fun configureCodegen(
project: Project,
localExtension: ReactExtension,
rootExtension: PrivateReactExtension,
isLibrary: Boolean,
) {
// 首先,我们需要设置 Codegen 的输出目录
val generatedSrcDir: Provider<Directory> =
project.layout.buildDirectory.dir("generated/source/codegen")
// 我们为 jsRootDir(JS根目录)指定默认值(约定)。
// 对于 App 来说是根文件夹(即 Gradle 项目的 ../../)
// 对于 Library 来说是包文件夹(即 Gradle 项目的 ../)
if (isLibrary) {
localExtension.jsRootDir.convention(project.layout.projectDirectory.dir("../"))
} else {
localExtension.jsRootDir.convention(localExtension.root)
}
// 我们创建任务以从 JS 文件生成 Schema
val generateCodegenSchemaTask =
project.tasks.register(
"generateCodegenSchemaFromJavaScript",
GenerateCodegenSchemaTask::class.java,
) { it ->
it.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
it.codegenDir.set(rootExtension.codegenDir)
it.generatedSrcDir.set(generatedSrcDir)
it.nodeWorkingDir.set(project.layout.projectDirectory.asFile.absolutePath)
// 我们在配置阶段读取 package.json,以便正确地填充此任务的 `jsRootDir` @Input 属性
// 以及 onlyIf 条件。因此,parsePackageJson 应该在这个 lambda 表达式内部被调用。
val packageJson = findPackageJsonFile(project, rootExtension.root)
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
val jsSrcsDirInPackageJson = parsedPackageJson?.codegenConfig?.jsSrcsDir
val includesGeneratedCode =
parsedPackageJson?.codegenConfig?.includesGeneratedCode ?: false
if (jsSrcsDirInPackageJson != null) {
it.jsRootDir.set(File(packageJson.parentFile, jsSrcsDirInPackageJson))
} else {
it.jsRootDir.set(localExtension.jsRootDir)
}
it.jsInputFiles.set(
project.fileTree(it.jsRootDir) { tree ->
tree.include("**/*.js")
tree.include("**/*.jsx")
tree.include("**/*.ts")
tree.include("**/*.tsx")
tree.exclude("node_modules/**/*")
tree.exclude("**/*.d.ts")
// 我们希望排除 build 目录,以避免在执行规避检查时选中它们
tree.exclude("**/build/**/*")
}
)
val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(rootExtension.root)
it.onlyIf { (isLibrary || needsCodegenFromPackageJson) && !includesGeneratedCode }
}
// 我们创建任务以从 Schema 生成 Java 代码
val generateCodegenArtifactsTask =
project.tasks.register(
"generateCodegenArtifactsFromSchema",
GenerateCodegenArtifactsTask::class.java,
) { task ->
task.dependsOn(generateCodegenSchemaTask)
task.reactNativeDir.set(rootExtension.reactNativeDir)
task.nodeExecutableAndArgs.set(rootExtension.nodeExecutableAndArgs)
task.generatedSrcDir.set(generatedSrcDir)
task.packageJsonFile.set(findPackageJsonFile(project, rootExtension.root))
task.codegenJavaPackageName.set(localExtension.codegenJavaPackageName)
task.libraryName.set(localExtension.libraryName)
task.nodeWorkingDir.set(project.layout.projectDirectory.asFile.absolutePath)
// 请注意,appNeedsCodegen 会触发在配置阶段读取 package.json,
// 因为我们需要填充此任务的 onlyIf 条件。
// 因此,appNeedsCodegen 需要在这个 lambda 表达式内部被调用
val needsCodegenFromPackageJson = project.needsCodegenFromPackageJson(rootExtension.root)
val packageJson = findPackageJsonFile(project, rootExtension.root)
val parsedPackageJson = packageJson?.let { JsonUtils.fromPackageJson(it) }
val includesGeneratedCode =
parsedPackageJson?.codegenConfig?.includesGeneratedCode ?: false
task.onlyIf { (isLibrary || needsCodegenFromPackageJson) && !includesGeneratedCode }
}
// 我们更新 Android 配置以包含生成的源码
// 这相当于以下的 DSL:
//
// android { sourceSets { main { java { srcDirs += "$generatedSrcDir/java" } } } }
if (isLibrary) {
project.extensions.getByType(LibraryAndroidComponentsExtension::class.java).finalizeDsl { ext
->
ext.sourceSets.getByName("main").java.srcDir(generatedSrcDir.get().dir("java").asFile)
}
} else {
project.extensions.getByType(ApplicationAndroidComponentsExtension::class.java).finalizeDsl {
ext ->
ext.sourceSets.getByName("main").java.srcDir(generatedSrcDir.get().dir("java").asFile)
}
}
// `preBuild` 是 AGP 自动注册的基础任务之一
// 这将在编译整个项目之前调用 Codegen
project.tasks.named("preBuild", Task::class.java).dependsOn(generateCodegenArtifactsTask)
}
configureCodegen 是 React Native Gradle 插件的核心方法,负责配置 Fabric/TurboModule 新架构的代码生成(Codegen)流程。该方法在 Gradle 配置阶段执行,设置两个关键任务和 Android 源码集集成。
其流程可以分为五个阶段:
-
设置输出目录和 JS 根目录
-
创建 Schema 生成任务
-
创建代码生成任务
-
集成到 Android 构建系统
-
挂载到构建生命周期
这里概括一下整个流程:
1. Gradle 配置阶段
├─> 设置 generatedSrcDir = "build/generated/source/codegen"
├─> 设置 jsRootDir 默认值(Library: "../", App: root)
├─> 读取 package.json(配置阶段)
│ ├─> 检查 codegenConfig.jsSrcsDir
│ └─> 检查 codegenConfig.includesGeneratedCode
├─> 注册 generateCodegenSchemaTask
│ ├─> 配置 Node.js 环境
│ ├─> 设置 JS 输入文件(**/*.{js,jsx,ts,tsx})
│ └─> 设置 onlyIf 条件
├─> 注册 generateCodegenArtifactsTask
│ ├─> 依赖 generateCodegenSchemaTask
│ └─> 设置 onlyIf 条件
├─> 配置 Android SourceSets
│ └─> 添加 generatedSrcDir/java 到 main SourceSet
└─> 建立 preBuild 依赖关系
2. Gradle 执行阶段(运行 ./gradlew build)
├─> preBuild 任务执行
│ └─> generateCodegenArtifactsTask 执行
│ ├─> generateCodegenSchemaTask 执行
│ │ ├─> 扫描 JS/TS 文件
│ │ └─> 生成 schema.json
│ └─> 根据 schema.json 生成 Java/C++ 代码
└─> compileJava 编译生成的代码
这里的两个关键任务分别是generateCodegenSchemaFromJavaScript 和generateCodegenArtifactsFromSchema。前者从 JS/TS 生成 Schema JSON文件,后者从 Schema 生成 Java/C++ 代码。
Schema JSON主要是JS/TS的接口描述,生成路径是Your Project/node_modules/Third-party Lib/android/build/generated/source/codegen/schema.json,可以自行查看,这里我们重点关注generateCodegenArtifactsFromSchema,源码react-native/packages/gradle-plugin/react-native-gradle-plugin/src/main/kotlin/com/facebook/react/tasks/GenerateCodegenArtifactsTask.kt:
kotlin
abstract class GenerateCodegenArtifactsTask : Exec() {
@get:Internal abstract val reactNativeDir: DirectoryProperty
@get:Internal abstract val generatedSrcDir: DirectoryProperty
@get:InputFile abstract val packageJsonFile: RegularFileProperty // package.json 文件路径
@get:Input abstract val nodeWorkingDir: Property<String>
@get:Input abstract val nodeExecutableAndArgs: ListProperty<String>
@get:Input abstract val codegenJavaPackageName: Property<String>
@get:Input abstract val libraryName: Property<String>
@get:InputFile
val generatedSchemaFile: Provider<RegularFile> = generatedSrcDir.file("schema.json")
@get:OutputDirectory val generatedJavaFiles: Provider<Directory> = generatedSrcDir.dir("java")
@get:OutputDirectory val generatedJniFiles: Provider<Directory> = generatedSrcDir.dir("jni")
override fun exec() {
val (resolvedLibraryName, resolvedCodegenJavaPackageName) = resolveTaskParameters()
setupCommandLine(resolvedLibraryName, resolvedCodegenJavaPackageName)
super.exec()
}
internal fun resolveTaskParameters(): Pair<String, String> {
val parsedPackageJson =
if (packageJsonFile.isPresent && packageJsonFile.get().asFile.exists()) {
JsonUtils.fromPackageJson(packageJsonFile.get().asFile)
} else {
null
}
val resolvedLibraryName = parsedPackageJson?.codegenConfig?.name ?: libraryName.get()
val resolvedCodegenJavaPackageName =
parsedPackageJson?.codegenConfig?.android?.javaPackageName ?: codegenJavaPackageName.get()
return resolvedLibraryName to resolvedCodegenJavaPackageName
}
internal fun setupCommandLine(libraryName: String, codegenJavaPackageName: String) {
val workingDir = File(nodeWorkingDir.get())
commandLine(
windowsAwareCommandLine(
*nodeExecutableAndArgs.get().toTypedArray(),
reactNativeDir.file("scripts/generate-specs-cli.js").get().asFile.cliPath(workingDir),
"--platform",
"android",
"--schemaPath",
generatedSchemaFile.get().asFile.cliPath(workingDir),
"--outputDir",
generatedSrcDir.get().asFile.cliPath(workingDir),
"--libraryName",
libraryName,
"--javaPackageName",
codegenJavaPackageName,
)
)
}
}
该类主要用于执行外部 Node.js 命令,依据 schema.json文件,生成 Java/JNI 代码。需要注意一下,这里libraryName和codegenJavaPackageName的取值,代码中是通过resolveTaskParameters方法提取出来的。回顾一下package.json文件的配置,大概是以下结构:
json
{
"codegenConfig": {
"name": "MyLibrary",
"android": {
"javaPackageName": "com.example.mylibrary"
}
}
}
那么libraryName就是"MyLibrary",codegenJavaPackageName就是"com.example.mylibrary"。
现在再还原一下命令,其实就是以下形式:
shell
node <reactNativeDir>/scripts/generate-specs-cli.js \
--platform android \
--schemaPath <generatedSrcDir>/schema.json \
--outputDir <generatedSrcDir> \
--libraryName <resolvedLibraryName> \
--javaPackageName <resolvedCodegenJavaPackageName>
我们定位到该js文件react-native/packages/react-native/scripts/generate-specs-cli.js:
js
const executor = require('./codegen/generate-specs-cli-executor');
// 省略......
function main() {
executor.execute(
// $FlowFixMe[prop-missing]
argv.platform,
// $FlowFixMe[prop-missing]
argv.schemaPath,
// $FlowFixMe[prop-missing]
argv.outputDir,
// $FlowFixMe[prop-missing]
argv.libraryName,
// $FlowFixMe[prop-missing]
argv.javaPackageName,
// $FlowFixMe[prop-missing]
argv.libraryType,
);
}
main();
继续跟踪react-native/packages/react-native/scripts/codegen/generate-specs-cli-executor.js
js
const utils = require('./codegen-utils');
const GENERATORS /*: {[string]: {[string]: $ReadOnlyArray<string>}} */ = {
all: {
android: ['componentsAndroid', 'modulesAndroid', 'modulesCxx'],
ios: ['componentsIOS', 'modulesIOS', 'modulesCxx'],
},
components: {
android: ['componentsAndroid'],
ios: ['componentsIOS'],
},
modules: {
android: ['modulesAndroid', 'modulesCxx'],
ios: ['modulesIOS', 'modulesCxx'],
},
};
function generateSpecFromInMemorySchema(
platform /*: string */,
schema /*: string */,
outputDirectory /*: string */,
libraryName /*: string */,
packageName /*: string */,
libraryType /*: string */,
useLocalIncludePaths /*: boolean */,
) {
validateLibraryType(libraryType);
createOutputDirectoryIfNeeded(outputDirectory, libraryName);
const includeGetDebugPropsImplementation =
libraryName.includes('FBReactNativeSpec'); //only generate getDebugString for React Native Core Components
utils.getCodegen().generate(
{
libraryName,
schema,
outputDirectory,
packageName,
assumeNonnull: platform === 'ios',
useLocalIncludePaths,
includeGetDebugPropsImplementation,
},
{
generators: GENERATORS[libraryType][platform],
},
);
if (platform === 'android') {
// Move all components C++ files to a structured jni folder for now.
// Note: this should've been done by RNCodegen's generators, but:
// * the generators don't support platform option yet
// * this subdir structure is Android-only, not applicable to iOS
const files = fs.readdirSync(outputDirectory);
const jniOutputDirectory = `${outputDirectory}/jni/react/renderer/components/${libraryName}`;
fs.mkdirSync(jniOutputDirectory, {recursive: true});
files
.filter(f => f.endsWith('.h') || f.endsWith('.cpp'))
.forEach(f => {
fs.renameSync(`${outputDirectory}/${f}`, `${jniOutputDirectory}/${f}`);
});
}
}
这里核心是调用utils.getCodegen().generate来执行代码生成逻辑,但是这里有一个传参需要注意一下,generators: GENERATORS[libraryType][platform],我们观察GENERATORS 的定义就会发现,这里的参数配置正好对应package.json中的codegen配置,由于我们现在研究的是Fabric组件注册,那么这里的参数应该是componentsAndroid。
继续查找generate方法的实现,源码react-native/packages/react-native/scripts/codegen/codegen-utils.js:
js
/**
* 用于抽象实际代码生成过程的包装器。
* 之所以需要这个包装器,是因为在 Sandcastle 中运行测试时,并非所有环境都像通常那样设置。
* 例如,`@react-native/codegen` 库就不存在。
*
* 借助这个包装器,我们可以模拟代码生成器的 getter 方法,使其返回一个自定义对象,该对象模拟了 Codegen 接口。
*
* @return 一个可以为新架构生成代码的对象。
*/
function getCodegen() /*: $FlowFixMe */ {
let RNCodegen;
try {
// $FlowFixMe[cannot-resolve-module]
RNCodegen = require('../../packages/react-native-codegen/lib/generators/RNCodegen.js');
} catch (e) {
// $FlowFixMe[cannot-resolve-module]
RNCodegen = require('@react-native/codegen/lib/generators/RNCodegen.js');
}
if (!RNCodegen) {
throw new Error('RNCodegen not found.');
}
return RNCodegen;
}
继续跟踪react-native/packages/react-native-codegen/lib/generators/RNCodegen.js:
js
const LIBRARY_GENERATORS = {
descriptors: [
generateComponentDescriptorCpp.generate,
generateComponentDescriptorH.generate,
],
events: [generateEventEmitterCpp.generate, generateEventEmitterH.generate],
states: [generateStateCpp.generate, generateStateH.generate],
props: [
generateComponentHObjCpp.generate,
generatePropsCpp.generate,
generatePropsH.generate,
generatePropsJavaInterface.generate,
generatePropsJavaDelegate.generate,
],
// TODO: Refactor this to consolidate various C++ output variation instead of forking per platform.
componentsAndroid: [
// JNI/C++ files
generateComponentDescriptorH.generate,
generateComponentDescriptorCpp.generate,
generateEventEmitterCpp.generate,
generateEventEmitterH.generate,
generatePropsCpp.generate,
generatePropsH.generate,
generateStateCpp.generate,
generateStateH.generate,
generateShadowNodeCpp.generate,
generateShadowNodeH.generate,
// Java files
generatePropsJavaInterface.generate,
generatePropsJavaDelegate.generate,
],
componentsIOS: [
generateComponentDescriptorH.generate,
generateComponentDescriptorCpp.generate,
generateEventEmitterCpp.generate,
generateEventEmitterH.generate,
generateComponentHObjCpp.generate,
generatePropsCpp.generate,
generatePropsH.generate,
generateStateCpp.generate,
generateStateH.generate,
generateShadowNodeCpp.generate,
generateShadowNodeH.generate,
],
modulesAndroid: [
generateModuleJniCpp.generate,
generateModuleJniH.generate,
generateModuleJavaSpec.generate,
],
modulesCxx: [generateModuleH.generate],
modulesIOS: [generateModuleObjCpp.generate],
tests: [generateTests.generate],
'shadow-nodes': [
generateShadowNodeCpp.generate,
generateShadowNodeH.generate,
],
};
module.exports = {
allGenerators: ALL_GENERATORS,
generate(
{
libraryName,
schema,
outputDirectory,
packageName,
assumeNonnull,
useLocalIncludePaths,
includeGetDebugPropsImplementation = false,
libraryGenerators = LIBRARY_GENERATORS,
},
{generators, test},
) {
schemaValidator.validate(schema);
const defaultHeaderPrefix = 'react/renderer/components';
const headerPrefix =
useLocalIncludePaths === true
? ''
: `${defaultHeaderPrefix}/${libraryName}/`;
function composePath(intermediate) {
return path.join(outputDirectory, intermediate, libraryName);
}
const componentIOSOutput = composePath(
useLocalIncludePaths === true ? '' : defaultHeaderPrefix,
);
const modulesIOSOutput = composePath('./');
const outputFoldersForGenerators = {
componentsIOS: componentIOSOutput,
modulesIOS: modulesIOSOutput,
descriptors: outputDirectory,
events: outputDirectory,
props: outputDirectory,
states: outputDirectory,
componentsAndroid: outputDirectory,
modulesAndroid: outputDirectory,
modulesCxx: outputDirectory,
tests: outputDirectory,
'shadow-nodes': outputDirectory,
};
const generatedFiles = [];
for (const name of generators) {
for (const generator of libraryGenerators[name]) {
generator(
libraryName,
schema,
packageName,
assumeNonnull,
headerPrefix,
includeGetDebugPropsImplementation,
).forEach((contents, fileName) => {
generatedFiles.push({
name: fileName,
content: contents,
outputDir: outputFoldersForGenerators[name],
});
});
}
}
return checkOrWriteFiles(generatedFiles, test);
},
// 省略......
};
可以看到,这里for (const name of generators)遍历的generators 就是我们前面强调过的GENERATORS 参数处理。那么此时name的值就是componentsAndroid。
既然确定了参数,那么从libraryGenerators中遍历出来的generator就是以下这些生成器:
js
componentsAndroid: [
// JNI/C++ files
generateComponentDescriptorH.generate,
generateComponentDescriptorCpp.generate,
generateEventEmitterCpp.generate,
generateEventEmitterH.generate,
generatePropsCpp.generate,
generatePropsH.generate,
generateStateCpp.generate,
generateStateH.generate,
generateShadowNodeCpp.generate,
generateShadowNodeH.generate,
// Java files
generatePropsJavaInterface.generate,
generatePropsJavaDelegate.generate,
],
这里先研究一下generateComponentDescriptorH.generate和generateComponentDescriptorCpp.generate。
源码react-native/packages/react-native-codegen/src/generators/components/GenerateComponentDescriptorH.js:
js
const FileTemplate = ({
libraryName,
componentDefinitions,
headerPrefix,
}: {
libraryName: string,
componentDefinitions: string,
headerPrefix: string,
}) => `
/**
* This code was generated by [react-native-codegen](https://www.npmjs.com/package/react-native-codegen).
*
* Do not edit this file as changes may cause incorrect behavior and will be lost
* once the code is regenerated.
*
* ${'@'}generated by codegen project: GenerateComponentDescriptorH.js
*/
#pragma once
${IncludeTemplate({headerPrefix, file: 'ShadowNodes.h'})}
#include <react/renderer/core/ConcreteComponentDescriptor.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
namespace facebook::react {
${componentDefinitions}
void ${libraryName}_registerComponentDescriptorsFromCodegen(
std::shared_ptr<const ComponentDescriptorProviderRegistry> registry);
} // namespace facebook::react
`;
const ComponentDefinitionTemplate = ({className}: {className: string}) =>
`
using ${className}ComponentDescriptor = ConcreteComponentDescriptor<${className}ShadowNode>;
`.trim();
// 省略部分代码......
这里我们只简单看一下用于头文件生成的字符串模版。libraryName参数取值我们上面已经分析过了,来自package.json中的配置项。那么还有一个关键参数className的取值需要弄清楚。实际上这里的componentDefinitions和className都来自于schema.json。具体看一下className生成逻辑:
js
const componentDefinitions = Object.keys(schema.modules)
.map(moduleName => {
const module = schema.modules[moduleName];
if (module.type !== 'Component') {
return;
}
const {components} = module;
// No components in this module
if (components == null) {
return null;
}
return Object.keys(components)
.map(componentName => {
if (components[componentName].interfaceOnly === true) {
return;
}
return ComponentDefinitionTemplate({className: componentName});
})
.join('\n');
})
.filter(Boolean)
.join('\n');
const replacedTemplate = FileTemplate({
libraryName,
componentDefinitions,
headerPrefix: headerPrefix ?? '',
});
可以看到,实际上是在遍历schema.json中的components字段。在声明组件一节已经创建了Demo工程,现在构建项目生成schema.json,查看相关内容:
json
{
"libraryName": "",
"modules": {
"CustomWebView": {
"type": "Component",
"components": {
"CustomWebView": {
"extendsProps": [
{
"type": "ReactNativeBuiltInType",
"knownTypeName": "ReactNativeCoreViewProps"
}
],
"events": [省略......],
"props": [省略......],
"commands": []
}
}
}
}
}
那么className就是componentName,也就是自定义的组件名CustomWebView。
C++ 层
弄清楚代码生成的逻辑之后,接下来我们可以直接查看生成的文件内容,主要是ComponentDescriptors.h
cpp
#pragma once
#include <react/renderer/components/CustomviewViewSpec/ShadowNodes.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
namespace facebook::react {
using CustomWebViewComponentDescriptor = ConcreteComponentDescriptor<CustomWebViewShadowNode>;
void CustomviewViewSpec_registerComponentDescriptorsFromCodegen(
std::shared_ptr<const ComponentDescriptorProviderRegistry> registry);
}
ComponentDescriptors.cpp
cpp
#include <react/renderer/components/CustomviewViewSpec/ComponentDescriptors.h>
#include <react/renderer/core/ConcreteComponentDescriptor.h>
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
namespace facebook::react {
void CustomviewViewSpec_registerComponentDescriptorsFromCodegen(
std::shared_ptr<const ComponentDescriptorProviderRegistry> registry) {
registry->add(concreteComponentDescriptorProvider<CustomWebViewComponentDescriptor>());
}
}
头文件定义了一个类型别名CustomWebViewComponentDescriptor,然后在实现文件中注册了这个Provider。我们看一下concreteComponentDescriptorProvider函数的实现,源码react-native/packages/react-native/ReactCommon/react/renderer/componentregistry/ComponentDescriptorProvider.h:
cpp
/*
* 为给定的 `ComponentDescriptor` 类创建一个 `ComponentDescriptorProvider`
*/
template <typename ComponentDescriptorT>
ComponentDescriptorProvider concreteComponentDescriptorProvider()
{
static_assert(
std::is_base_of<ComponentDescriptor, ComponentDescriptorT>::value,
"ComponentDescriptorT must be a descendant of ComponentDescriptor");
return {
ComponentDescriptorT::ConcreteShadowNode::Handle(),
ComponentDescriptorT::ConcreteShadowNode::Name(),
nullptr,
&concreteComponentDescriptorConstructor<ComponentDescriptorT>};
}
返回值是一个ComponentDescriptorProvider类型实例,继续跟踪一下类定义:
cpp
/*
* 提供了一种统一的方式来构造特定存储的 `ComponentDescriptor` 类的实例。
* C++ 不允许创建指向构造函数的指针,因此我们必须使用这样的数据结构来操作一组类。
*
* 注意:某些组件的 `handle` 和 `name` 的实际值取决于 `flavor`。
* 如果使用给定的 `flavor` 通过 `constructor` 对象实例化后,
* Provider暴露的 `handle` 和 `name` 值与预期值相同,则该提供者有效。
*/
class ComponentDescriptorProvider final {
public:
ComponentHandle handle;
ComponentName name;
ComponentDescriptor::Flavor flavor;
ComponentDescriptorConstructor *constructor;
};
这里大量使用了C++模版,要想查看真正的handle和name值,需要当实际的模版类型中查找。这里先查看源码react-native/packages/react-native/ReactCommon/react/renderer/core/ConcreteComponentDescriptor.h:
cpp
namespace facebook::react {
/*
* Default template-based implementation of ComponentDescriptor.
* Use your `ShadowNode` type as a template argument and override any methods
* if necessary.
*/
template <typename ShadowNodeT>
class ConcreteComponentDescriptor : public ComponentDescriptor {
static_assert(std::is_base_of<ShadowNode, ShadowNodeT>::value, "ShadowNodeT must be a descendant of ShadowNode");
using SharedShadowNodeT = std::shared_ptr<const ShadowNodeT>;
public:
using ConcreteShadowNode = ShadowNodeT;
// 省略代码......
} // namespace facebook::react
可以看到,ConcreteShadowNode实际上只是一个类型别名,具体的要看模版的实际参数,那么ConcreteShadowNode::Handle就相当于CustomWebViewShadowNode::Handle。这里CustomWebViewShadowNode也是自动生成的代码,我们直接查看android/app/build/generated/source/codegen/jni/react/renderer/components/CustomviewViewSpec/ShadowNodes.h:
cpp
/*
* `ShadowNode` for <CustomWebView> component.
*/
using CustomWebViewShadowNode = ConcreteViewShadowNode<
CustomWebViewComponentName,
CustomWebViewProps,
CustomWebViewEventEmitter,
CustomWebViewState>;
继续查看android/app/build/generated/source/codegen/jni/react/renderer/components/CustomviewViewSpec/ShadowNodes.cpp:
cpp
#include <react/renderer/components/CustomviewViewSpec/ShadowNodes.h>
namespace facebook::react {
extern const char CustomWebViewComponentName[] = "CustomWebView";
}
现在跟踪一下react-native/packages/react-native/ReactCommon/react/renderer/components/view/ConcreteViewShadowNode.h:
cpp
namespace facebook::react {
/*
* Template for all <View>-like classes (classes which have all same props
* as <View> and similar basic behaviour).
* For example: <Paragraph>, <Image>, but not <Text>, <RawText>.
*/
template <
const char *concreteComponentName,
typename ViewPropsT = ViewProps,
typename ViewEventEmitterT = ViewEventEmitter,
typename StateDataT = StateData>
requires(std::is_base_of_v<ViewProps, ViewPropsT>)
class ConcreteViewShadowNode : public ConcreteShadowNode<
concreteComponentName,
YogaLayoutableShadowNode,
ViewPropsT,
ViewEventEmitterT,
StateDataT> {
// 省略代码......
};
} // namespace facebook::react
ConcreteViewShadowNode类中并未实现Handle和Name方法,继续查看父类react-native/packages/react-native/ReactCommon/react/renderer/core/ConcreteShadowNode.h
cpp
namespace facebook::react {
/*
* Base templace class for all `ShadowNode`s which connects exact `ShadowNode`
* type with exact `Props` type.
* `ConcreteShadowNode` is a default implementation of `ShadowNode` interface
* with many handy features.
*/
template <
ComponentName concreteComponentName,
typename BaseShadowNodeT,
typename PropsT,
typename EventEmitterT = EventEmitter,
typename StateDataT = StateData>
class ConcreteShadowNode : public BaseShadowNodeT {
protected:
using ShadowNode::props_;
using ShadowNode::state_;
public:
using BaseShadowNodeT::BaseShadowNodeT;
// 省略......
static ComponentName Name()
{
return ComponentName(concreteComponentName);
}
static ComponentHandle Handle()
{
return ComponentHandle(concreteComponentName);
}
// 省略......
};
} // namespace facebook::react
到这里就很清晰了,Name和Handle方法返回值内部是持有的相同的concreteComponentName,而这个模版参数,根据前面的传参,实际是就是
CustomWebViewComponentName,也就是"CustomWebView"。
扫描Fabric 组件库,生成代码的逻辑其实已经很清楚了,最后只剩下一个问题,真正调用注册的代码在哪里?事实上,安卓中并未真正通过CustomviewViewSpec_registerComponentDescriptorsFromCodegen函数去注册,而是使用了autolinking机制。这部分在《Android端TurboModule分析》一文有详细介绍了,可以去回顾一下GenerateAutolinkingNewArchitecturesFileTask脚本的分析,其中生成的信息源就来自我们前面费了半天劲分析的autolinking.json中的
现在来看一下Gradle脚本生成的autolinking.cpp:
cpp
#include "autolinking.h"
#include <CustomviewViewSpec.h>
#include <react/renderer/components/CustomviewViewSpec/ComponentDescriptors.h>
namespace facebook {
namespace react {
std::shared_ptr<TurboModule> autolinking_ModuleProvider(const std::string moduleName, const JavaTurboModule::InitParams ¶ms) {
auto module_CustomviewViewSpec = CustomviewViewSpec_ModuleProvider(moduleName, params);
if (module_CustomviewViewSpec != nullptr) {
return module_CustomviewViewSpec;
}
return nullptr;
}
std::shared_ptr<TurboModule> autolinking_cxxModuleProvider(const std::string moduleName, const std::shared_ptr<CallInvoker>& jsInvoker) {
return nullptr;
}
void autolinking_registerProviders(std::shared_ptr<ComponentDescriptorProviderRegistry const> providerRegistry) {
providerRegistry->add(concreteComponentDescriptorProvider<CustomWebViewComponentDescriptor>());
return;
}
} // namespace react
} // namespace facebook
这里生成了autolinking_registerProviders方法,这才是真正注册组件的地方。而此处的代码是由Gradle脚本生成,其中的关键信息就来自autolinking.json中的componentDescriptors字段,也就是前面我们费了半天劲才分析出该字段的默认值的地方。整个React Native的代码生成其实是有些混乱的,会生成一些并不会使用的代码,对理解产生干扰。
关于autolinking_registerProviders函数的调用链,在前面的文章也分析涉及过,这里再回顾一下调用流程,源码react-native/packages/react-native/ReactAndroid/cmake-utils/default-app-setup/OnLoad.cpp:
cpp
namespace facebook::react {
void registerComponents(
std::shared_ptr<const ComponentDescriptorProviderRegistry> registry) {
// 自定义 Fabric 组件放在这里。您可以在此处注册来自您的应用程序或第三方库的自定义组件
// providerRegistry->add(concreteComponentDescriptorProvider<
// MyComponentDescriptor>());
// We link app local components if available
#ifdef REACT_NATIVE_APP_COMPONENT_REGISTRATION
REACT_NATIVE_APP_COMPONENT_REGISTRATION(registry);
#endif
// And we fallback to the components autolinked
autolinking_registerProviders(registry);
}
// 省略部分代码......
} // namespace facebook::react
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
return facebook::jni::initialize(vm, [] {
facebook::react::DefaultTurboModuleManagerDelegate::cxxModuleProvider =
&facebook::react::cxxModuleProvider;
facebook::react::DefaultTurboModuleManagerDelegate::javaModuleProvider =
&facebook::react::javaModuleProvider;
facebook::react::DefaultComponentsRegistry::
registerComponentDescriptorsFromEntryPoint =
&facebook::react::registerComponents;
});
}
可见,注册的起点也是JNI_OnLoad函数。
Kotlin层
使用npx create-react-native-library@latest工具创建一个Fabric组件库时,会生成一些模版代码,其中包括我们上面提到的CustomWebViewManager类,现在我们来看一下CustomWebViewPackage类:
kotlin
class CustomWebViewPackage : ReactPackage {
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
val viewManagers: MutableList<ViewManager<*, *>> = ArrayList()
viewManagers.add(CustomWebViewManager())
return viewManagers
}
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return emptyList()
}
}
这里createViewManagers方法会返回一个ViewManager的实例列表。根据我们在《ReactNative新架构之Android端TurboModule机制完全解析》一文中分析GeneratePackageListTask任务的结果,我们知道最终会生成一个PackageList文件,其中会注入每个三方TurboModule或Fabric组件包中的ReactPackage实现类。
现在来查看一下我们示例工程生成的example/android/app/build/generated/autolinking/src/main/java/com/facebook/react/PackageList.java文件:
java
public class PackageList {
private Application application;
private ReactNativeHost reactNativeHost;
private MainPackageConfig mConfig;
// 省略部分代码......
public ArrayList<ReactPackage> getPackages() {
return new ArrayList<>(Arrays.<ReactPackage>asList(
new MainReactPackage(mConfig),
new CustomWebViewPackage()
));
}
}
回顾一下本文开头的初始化部分,我们提到过以下代码
kotlin
fabricUIManager =
FabricUIManager(context, ViewManagerRegistry(viewManagerResolver), eventBeatManager)
现在回顾一下《React Native新架构之Android端初始化源码分析》一文,在ReactInstance类构造时,有如下初始化逻辑:
kotlin
viewManagerResolver = BridgelessViewManagerResolver(reactPackages, context)
ComponentNameResolverBinding.install(
unbufferedRuntimeExecutor,
object : ComponentNameResolver {
override val componentNames: Array<String>
get() {
val viewManagerNames = viewManagerResolver.getViewManagerNames()
if (viewManagerNames.isEmpty()) {
FLog.e(TAG, "No ViewManager names found")
return arrayOf()
}
return viewManagerNames.toTypedArray<String>()
}
},
)
结合上一节查找组件 kotlin层实现的分析,整个流程都十分清晰了。