React Native之Android端Fabric 架构源码分析(下)

39 阅读18分钟

React Native之Android端Fabric 架构源码分析(下)

由于单章字数限制,这里将本文拆成上下两篇,上篇《React Native之Android端Fabric 架构源码分析(上)》

Fabric 组件

声明组件

要开发一个Fabric 组件,首先需要在TS代码中声明规范。我们使用npx create-react-native-library@latest创建一个Fabric组件库,然后参考根据官方文档的示例添加如下内容:

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:

// 如果这个函数运行,说明视图配置没有在构建时通过 `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类型定义的工具包:

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

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

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中的模板,大概就能还原出生成的代码是什么样子:

// 输入:
// 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 组件规范进行了代码替换:

// 开发者编写
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

  get NativeComponentRegistry() {
    return require('./Libraries/NativeComponent/NativeComponentRegistry');
  },

定位到方法实现react-native/packages/react-native/Libraries/NativeComponent/NativeComponentRegistry.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

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

import * as AppRegistry from './AppRegistryImpl';

// 省略部分代码......
global.RN$AppRegistry = AppRegistry;

registerCallableModule('AppRegistry', AppRegistry);

export {AppRegistry};

继续跟踪源码react-native/packages/react-native/Libraries/ReactNative/AppRegistryImpl.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

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

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

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

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方法,它负责调度更新。由于代码量较大,我们后面只追求核心逻辑,将省略大部分代码:

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方法实现:

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方法完成渲染工作:

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

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方法:

/**
 * 获取指定视图的配置。如果这是第一次使用该视图,此配置将从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,也就是声明的组件名称,我们继续查看原生节点创建的方法:

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

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方法,直接查看UIManagerBindingget方法:

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实现:

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方法,回顾一下安卓原生组件的查找:

ViewManager viewManager = mViewManagerRegistry.get(componentName);

跟踪源码react-native/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManagerRegistry.kt

  @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文件中:

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

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

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

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

接下来我们回顾前文《ReactNative新架构之Android端TurboModule机制完全解析》,其中提到但略过的React Gradle脚本的configureCodegen方法:

  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 源码集集成。

其流程可以分为五个阶段:

  1. 设置输出目录和 JS 根目录

  2. 创建 Schema 生成任务

  3. 创建代码生成任务

  4. 集成到 Android 构建系统

  5. 挂载到构建生命周期

这里概括一下整个流程:

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 编译生成的代码

这里的两个关键任务分别是generateCodegenSchemaFromJavaScriptgenerateCodegenArtifactsFromSchema。前者从 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

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 代码。需要注意一下,这里libraryNamecodegenJavaPackageName的取值,代码中是通过resolveTaskParameters方法提取出来的。回顾一下package.json文件的配置,大概是以下结构:

{
  "codegenConfig": {
    "name": "MyLibrary",
    "android": {
      "javaPackageName": "com.example.mylibrary"
    }
  }
}

那么libraryName就是"MyLibrary"codegenJavaPackageName就是"com.example.mylibrary"

现在再还原一下命令,其实就是以下形式:

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

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

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

/**
* 用于抽象实际代码生成过程的包装器。 
* 之所以需要这个包装器,是因为在 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

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就是以下这些生成器:

  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.generategenerateComponentDescriptorCpp.generate

源码react-native/packages/react-native-codegen/src/generators/components/GenerateComponentDescriptorH.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的取值需要弄清楚。实际上这里的componentDefinitionsclassName都来自于schema.json。具体看一下className生成逻辑:

    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,查看相关内容:

{
  "libraryName": "",
  "modules": {
    "CustomWebView": {
      "type": "Component",
      "components": {
        "CustomWebView": {
          "extendsProps": [
            {
              "type": "ReactNativeBuiltInType",
              "knownTypeName": "ReactNativeCoreViewProps"
            }
          ],
          "events": [省略......],
          "props": [省略......],
          "commands": []
        }
      }
    }
  }
}

那么className就是componentName,也就是自定义的组件名CustomWebView

C++ 层

弄清楚代码生成的逻辑之后,接下来我们可以直接查看生成的文件内容,主要是ComponentDescriptors.h

#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

#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:

/*
* 为给定的 `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类型实例,继续跟踪一下类定义:

/*
 * 提供了一种统一的方式来构造特定存储的 `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

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

/*
 * `ShadowNode` for <CustomWebView> component.
 */
using CustomWebViewShadowNode = ConcreteViewShadowNode<
    CustomWebViewComponentName,
    CustomWebViewProps,
    CustomWebViewEventEmitter,
    CustomWebViewState>;

继续查看android/app/build/generated/source/codegen/jni/react/renderer/components/CustomviewViewSpec/ShadowNodes.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

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类中并未实现HandleName方法,继续查看父类react-native/packages/react-native/ReactCommon/react/renderer/core/ConcreteShadowNode.h

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

#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 &params) {
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

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类:

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文件:

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()
    ));
  }
}

回顾一下本文开头的初始化部分,我们提到过以下代码

fabricUIManager =
    FabricUIManager(context, ViewManagerRegistry(viewManagerResolver), eventBeatManager)

现在回顾一下《React Native新架构之Android端初始化源码分析》一文,在ReactInstance类构造时,有如下初始化逻辑:

    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层实现的分析,整个流程都十分清晰了。