2019-04-09
UIManagerModule 是 React Native 中视图展示的核心模块,通过挖掘 UIManagerModule,又发现了 JS 侧的 ReactNativeRenderer,最终将整个渲染流程串联起来。
UIManagerModule 是如何创建的呢?为了回答这个问题,有许多相关的概念需要介绍。
我们在写 Native Modules 是都需要创建一个 Package。React Native 内部也是使用 Package 机制来构成的。在 Package 内部包含有 Native Modules 和 ViewManagers。
在这些内置 Package 中,最重要的一个是 CoreModulesPackage。它为 React Native 提供了核心支撑能力。
CoreModulesPackage 中一个非常重要的模块就是 UIManagerModule,
UIManagerModule 的创建就是在 CoreModulesPackage 的 createUIManager 方法中。
在上一节中我们知道了 UIManagerModule 是 CoreModulesPackage 中的一个 Module。
因此接下来的问题是,在 React Native 中,谁会获取 UIManagerModule 呢?
首先 UIManagerModule 中一个常量 NAME
,作为这个模块的标识:
public static final String NAME = "UIManager";
我们可以通过这个线索入手。找到了这么几个地方:
其中:
在上面的调用放中,最引起我注意的是 ReactFabric。
在本节中我们来看看它,首先它不是最原始的代码,而是经过了一次 build,这个文件总共接近 2w 行。
我们直接看最底部:
var fabric = ReactFabric$3.default || ReactFabric$3; module.exports = fabric; })(); }
它对外导出了一个 fabric 对象,看起来跟 Fabric 重构有很大的关系。
第一个需要确认的是:ReactFabric 到底有没有执行呢?
最简单的方法是给它改改,我将上面代码加了一行 log:
var fabric = ReactFabric$3.default || ReactFabric$3; console.log("maxiee -> run here!") module.exports = fabric; })(); }
经过一番疯狂打 log 试探,我发现 ReactFabric 并没有走到,而是走到了 ReactNativeRenderer-dev.js。
我猜测 ReactNativeRenderer-dev.js 这个还是 Fabric 重构之前的,ReactFabric 还没有生效。
ReactFabric 我们就先不管它了,来看 ReactNativeRenderer。
UIManager 既然是一个 Native Module,他应该有一个在 JS 侧的映射类。
在 ReactNativeRenderer 中导入 UIManager 通过下面方法:
var UIManager = require("UIManager");
这个映射类在哪里呢?它位于 Libraries/ReactNative/UIManager.js。
我们先不细看它内部的实现,只知道它在 JS 侧提供了 UIManager 的映射即可。
让我们回到 ReactNativeRenderer。
在这个文件的提交记录中,我发现大多都是:
React sync for revisions ...
莫非这个文件里包含了 React?看到其中一行就真相大白了:
var React = require("react");
ReactNativeRenderer 引用了 React,估计是在底层进行了桥接功能。
去网上搜索了一下,找到一篇非常好的文章:
粗略地阅读了一下,可以知道:
在这里我们先从简单的来,ReactNativeRenderer 引用了 UIManager,它都是怎么用的呢?
在 ReactNativeRenderer 里找了一圈,我找到一个吸引我的方法 —— createInstance。
它的方法签名如下:
function createInstance( type, props, rootContainerInstance, hostContext, internalInstanceHandle ) {
省略一些中间代码,我们直取核心。调用 UIManager.createView 创建视图:
UIManager.createView( tag, // reactTag viewConfig.uiViewClassName, // viewName rootContainerInstance, // rootTag updatePayload // props );
这一步会最终调用到 Java 层 com.facebook.react.uimanager.UIManagerModule#createView。
在这里跟我们上一篇《React Native 代码阅读(十三):View 是如何创建的》 完美地串起来了。
在上一篇中,我们是通过 spy bridge 发现调用 com.facebook.react.uimanager.UIManagerModule#createView,而在这里发现是引用 Native Module 发现这个调用的,这说明什么呢?
这说明 Native Module 内部的机制是通过发送 bridge message 来告知 Java 层进行方法调用的。
这又能与更早的系列文章串起来,由点及线,说明我们要开始掌握了。
之后创建了一个 component:
var component = new ReactNativeFiberHostComponent(tag, viewConfig);
最终返回组件:
return component;
让我们再进一步来看,哪里调用了 createInstance?
在 completeWork 方法中调用了它。
谁调用了 completeWork 呢? completeUnitOfWork
谁调用了 completeUnitOfWork 呢?renderRoot 和 performUnitOfWork
谁调用了 performUnitOfWork 呢?workLoop
谁调用了 workLoop 呢?renderRoot 和 replayUnitOfWork
结合《「ReactNative」View创建过程浅析》我们知道 Component 的 render 方法最终会调到 renderRoot
因此通过这番跟踪,摸清了 ReactNativeRenderer 的一条主线
在上文中我们看到了 ReactNativeRenderer 引用了 React,那它是如何使用它的呢?
ReactNativeRenderer 虽然有两万行之多,但是实际上没几处直接使用 React 的地方,但是这一行吸引了我的注意:
var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
其中:
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED
这个变量很重要,通过它作为关键字能够搜到很多关于 React 代码阅读的资料。
有了前面这些 JS 侧的积累,下面我们再回到 Java 侧的 UIManagerModule 中,看看都提供了哪些 React Method:
UIManagerModule 向 JS 侧提供了这些方法,供 React Native Renderer 调用。
通过这里,我也明白了为啥 React 要拆包拆出来 React 和 React DOM 两个包。
在本文中,通过学习 ReactNativeRenderer 这个文件,终于将 React Native 的渲染流程给完整地串起来了。
经过这十四篇文章,我对 React Native 的整体框架由模糊变清晰了一些。在后续的文章中,我会让这个框架变得更加显化。
以下是我在学习代码过程中总结,可能与主题相关不大,但是也有价值。
舍不得删,列举在下方:
ReactPackage 是一个接口。它有详细的注释:
它是向 catalyst 框架提供额外功能的主要接口,通过以下几种方式:
它包含两个接口方法:
这个跟我们平时创建 NativeModule 的时候是一样的。
在学习代码的时候,我看到了两个类:LazyReactPackage 与 TurboReactPackage,他俩什么关系呢?
TurboReactPackage 是在 Fabric 重构中,基于 TurboModules 的新 ReactPackage,用于替换 LazyReactPackage。这个替换在最新的 React Native 代码中已经完成了,在实际运行时走的是 TurboReactPackage。
需要注意的是,他俩都实现了 ReactPackage。