// If we have a delegate, we need to call it; we pass a null list to // callNativeModules, since we know there are no native calls, without // calling into JS again. If no calls were made and there’s no delegate, // nothing happens, which is correct. callNativeModules(Value::makeNull(m_context)); } }

上面提到flush()方法调用MessageQueue.js的flushedQueue()方法,这是如何做到的呢?樂。

事实上这是由bindBridge()方法来完成的,bindBridge()从__fbBatchedBridge(__fbBatchedBridge也是被MessageQueue.js设置为全局变量,供C++层读取)对象中取出MessageQueue.js里的四个方法:

callFunctionReturnFlushedQueue()invokeCallbackAndReturnFlushedQueue()flushedQueue()callFunctionReturnResultAndFlushedQueue()

并分别存在三个C++变量中:

m_callFunctionReturnFlushedQueueJSm_invokeCallbackAndReturnFlushedQueueJSm_flushedQueueJSm_callFunctionReturnResultAndFlushedQueueJS

这样C++就可以调用这四个JS方法。

void JSCExecutor::bindBridge() throw(JSException) { SystraceSection s(“JSCExecutor::bindBridge”); std::call_once(m_bindFlag, [this] { auto global = Object::getGlobalObject(m_context); auto batchedBridgeValue = global.getProperty(“__fbBatchedBridge”); if (batchedBridgeValue.isUndefined()) { auto requireBatchedBridge = global.getProperty(“__fbRequireBatchedBridge”); if (!requireBatchedBridge.isUndefined()) { batchedBridgeValue = requireBatchedBridge.asObject().callAsFunction({}); } if (batchedBridgeValue.isUndefined()) { throw JSException(“Could not get BatchedBridge, make sure your bundle is packaged correctly”); } }

auto batchedBridge = batchedBridgeValue.asObject(); m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty(“callFunctionReturnFlushedQueue”).asObject(); m_invokeCallbackAndReturnFlushedQueueJS = batchedBridge.getProperty(“invokeCallbackAndReturnFlushedQueue”).asObject(); m_flushedQueueJS = batchedBridge.getProperty(“flushedQueue”).asObject(); m_callFunctionReturnResultAndFlushedQueueJS = batchedBridge.getProperty(“callFunctionReturnResultAndFlushedQueue”).asObject(); }); }

至此,JS Bundle已经加载解析完成,进入MessageQueue.js开始执行。

2.4 绑定ReactContext与ReactRootView

JS Bundle加载完成以后,前面说的createReactContext()就执行完成了,然后开始执行setupReacContext()方法,绑定ReactContext与ReactRootView。 我们来看一下它的实现。

public class ReactInstanceManager {

private void setupReactContext(final ReactApplicationContext reactContext) { //…

//执行Java Module的初始化 catalystInstance.initialize(); //通知ReactContext已经被创建爱女 mDevSupportManager.onNewReactContextCreated(reactContext); //内存状态回调设置 mMemoryPressureRouter.addMemoryPressureListener(catalystInstance); //复位生命周期 moveReactContextToCurrentLifecycleState();

ReactMarker.logMarker(ATTACH_MEASURED_ROOT_VIEWS_START); synchronized (mAttachedRootViews) { //将所有的ReactRootView与catalystInstance进行绑定 for (ReactRootView rootView : mAttachedRootViews) { attachRootViewToInstance(rootView, catalystInstance); } } //… }

private void attachRootViewToInstance( final ReactRootView rootView, CatalystInstance catalystInstance) { //… UIManagerModule uiManagerModule = catalystInstance.getNativeModule(UIManagerModule.class); //将ReactRootView作为根布局 final int rootTag = uiManagerModule.addRootView(rootView); rootView.setRootViewTag(rootTag); //执行JS的入口bundle页面 rootView.invokeJSEntryPoint(); //… } x }

setupReactContext()方法主要完成每个ReactRootView与catalystInstance的绑定,绑定的过程主要做两件事情:

将ReactRootView作为根布局.执行JS的入口bundle页面.

JS的页面入口我们可以设置mJSEntryPoint来自定义入口,如果不设置则是默认的入口AppRegistry。

private void defaultJSEntryPoint() { //… try { //… String jsAppModuleName = getJSModuleName(); catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams); } finally { Systrace.endSection(TRACE_TAG_REACT_JAVA_BRIDGE); } }

这里的调用方式实际上就是原生调用JS的方法,它调用的正是我们很熟悉的AppRegistry.js,AppRegistry.js调用runApplication()开始执行JS页面的渲染,最终转换为 Native UI显示在手机上。

到此为止,整个RN页面的启动流程就分析完了,我们接着来看看RN页面是如何渲染最终显示在手机上的。

三 渲染原理

上面我们也说了,RN页面的入口一般是AppRegistry.js,我们就从这个页面入手开始分析RN页面的渲染流程。先看一下RN页面的渲染流程序列图,如下所示:

这个流程比较长,其实主要是方法的调用链多,原理还是很简单的,我们先概括性的总结一下:

React Native将代码由JSX转化为JS组件,启动过程中利用instantiateReactComponent将ReactElement转化为复合组件ReactCompositeComponent与元组件ReactNativeBaseComponent,利用 ReactReconciler对他们进行渲染。UIManager.js利用C++层的Instance.cpp将UI信息传递给UIManagerModule.java,并利用UIManagerModule.java构建UI。UIManagerModule.java接收到UI信息后,将UI的操作封装成对应的Action,放在队列中等待执行。各种UI的操作,例如创建、销毁、更新等便在队列里完成,UI最终 得以渲染在屏幕上。

3.1 JavaScript层组件渲染

如上图所示AppRegistry.registerComponent用来注册组件,在该方法内它会调用AppRegistry.runApplication()来启动js的渲染流程。AppRegistry.runApplication() 会将传入的Component转换成ReactElement,并在外面包裹一层AppContaniner,AppContaniner主要用来提供一些debug工具(例如:红盒)。

如下所示:

function renderApplication( RootComponent: ReactClass, initialProps: Props, rootTag: any ) { invariant( rootTag, 'Expect to have a valid rootTag, instead got ', rootTag ); ReactNative.render( , rootTag ); }

我们抛开函数调用链,分析其中关键的部分,其他部分都是简单的函数调用。

ReactNativeMount.renderComponent()instantiateReactComponent.instantiateReactComponent(node, shouldHaveDebugID)

ReactNativeMount.renderComponent()

/**

@param {ReactComponent} instance Instance to render.@param {containerTag} containerView Handle to native view tag / renderComponent: function( nextElement: ReactElement<>, containerTag: number, callback?: ?(() => void) ): ?ReactComponent {

//将RectElement使用相同的TopLevelWrapper进行包裹 var nextWrappedElement = React.createElement( TopLevelWrapper, { child: nextElement } );

var topRootNodeID = containerTag; var prevComponent = ReactNativeMount._instancesByContainerID[topRootNodeID]; if (prevComponent) { var prevWrappedElement = prevComponent._currentElement; var prevElement = prevWrappedElement.props.child; if (shouldUpdateReactComponent(prevElement, nextElement)) { ReactUpdateQueue.enqueueElementInternal(prevComponent, nextWrappedElement, emptyObject); if (callback) { ReactUpdateQueue.enqueueCallbackInternal(prevComponent, callback); } return prevComponent; } else { ReactNativeMount.unmountComponentAtNode(containerTag); } }

if (!ReactNativeTagHandles.reactTagIsNativeTopRootID(containerTag)) { console.error(‘You cannot render into anything but a top root’); return null; }

ReactNativeTagHandles.assertRootTag(containerTag);

//检查之前的节点是否已经mount到目标节点上,如果有则进行比较处理 var instance = instantiateReactComponent(nextWrappedElement, false); ReactNativeMount._instancesByContainerID[containerTag] = instance;

// The initial render is synchronous but any updates that happen during // rendering, in componentWillMount or componentDidMount, will be batched // according to the current batching strategy.

//将mount任务提交给回调Queue,最终会调用ReactReconciler.mountComponent() ReactUpdates.batchedUpdates( batchedMountComponentIntoNode, instance, containerTag ); var component = instance.getPublicInstance(); if (callback) { callback.call(component); } return component; },

该方法主要做了以下事情:

将传入的RectElement使用相同的TopLevelWrapper进行包裹,生成nextWrappedElement。检查之前的节点是否已经mount到目标节点上,如果有则进行比较处理,将上一步生成的nextWrappedElement传入instantiateReactComponent(nextWrappedElement, false)方法。将mount任务提交给回调Queue,最终会调用ReactReconciler.mountComponent(),ReactReconciler.mountComponent()又会去调用C++层Instance::mountComponent() 方法。

instantiateReactComponent.instantiateReactComponent(node, shouldHaveDebugID)

在分析这个函数之前,我们先来补充一下React组件相关知识。React组件可以分为两种:

元组件:框架内置的,可以直接使用的组件。例如:View、Image等。它在React Native中用ReactNativeBaseComponent来描述。复合组件:用户封装的组件,一般可以通过React.createClass()来构建,提供render()方法来返回渲染目标。它在React Native中用ReactCompositeComponent来描述。

instantiateReactComponent(node, shouldHaveDebugID)方法根据对象的type生成元组件或者复合组件。

/**

Given a ReactNode, create an instance that will actually be mounted.@param {ReactNode} node@param {boolean} shouldHaveDebugID@return {object} A new instance of the element’s constructor.@protected */ function instantiateReactComponent(node, shouldHaveDebugID) { var instance;

if (node === null || node === false) { instance = ReactEmptyComponent.create(instantiateReactComponent); } else if (typeof node === ‘object’) { var element = node; var type = element.type;

if (typeof type !== ‘function’ && typeof type !== ‘string’) { var info = ‘’; if (process.env.NODE_ENV !== ‘production’) { if (type === undefined || typeof type === ‘object’ && type !== null && Object.keys(type).length === 0) { info += ’ You likely forgot to export your component from the file ’ + ‘it’s defined in.’; } } info += getDeclarationErrorAddendum(element._owner); !false ? process.env.NODE_ENV !== ‘production’ ? invariant(false, ‘Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: %s.%s’, type == null ? type : typeof type, info) : _prodInvariant(‘130’, type == null ? type : typeof type, info) : void 0; }

//如果对象的type为string,则调用ReactHostComponent.createInternalComponent(element)来注入生成组件的逻辑 if (typeof element.type === ‘string’) { instance = ReactHostComponent.createInternalComponent(element); } //如果是内部元组件,则创建一个type实例 else if (isInternalComponentType(element.type)) { // This is temporarily available for custom components that are not string // representations. I.e. ART. Once those are updated to use the string // representation, we can drop this code path. instance = new element.type(element);

// We renamed this. Allow the old name for compat.  if (!instance.getHostNode) { instance.getHostNode = instance.getNativeNode; } } //否则,则是用户创建的复合组件,这个时候创建一个ReactCompositeComponentWrapper实例,该实例用来描述复合组件 else { instance = new ReactCompositeComponentWrapper(element); } //当对象为string或者number时,调用ReactHostComponent.createInstanceForText(node)来注入组件生成逻辑。 } else if (typeof node === ‘string’ || typeof node === ‘number’) { instance = ReactHostComponent.createInstanceForText(node); } else { !false ? process.env.NODE_ENV !== ‘production’ ? invariant(false, ‘Encountered invalid React node of type %s’, typeof node) : _prodInvariant(‘131’, typeof node) : void 0; }

if (process.env.NODE_ENV !== ‘production’) { process.env.NODE_ENV !== ‘production’ ? warning(typeof instance.mountComponent === ‘function’ && typeof instance.receiveComponent === ‘function’ && typeof instance.getHostNode === ‘function’ && typeof instance.unmountComponent === ‘function’, ‘Only React Components can be mounted.’) : void 0; }

// These two fields are used by the DOM and ART diffing algorithms // respectively. Instead of using expandos on components, we should be // storing the state needed by the diffing algorithms elsewhere. instance._mountIndex = 0; instance._mountImage = null;

if (process.env.NODE_ENV !== ‘production’) { instance._debugID = shouldHaveDebugID ? getNextDebugID() : 0; }

// Internal instances should fully constructed at this point, so they should // not get any new fields added to them at this point. if (process.env.NODE_ENV !== ‘production’) { if (Object.preventExtensions) { Object.preventExtensions(instance); } }

return instance; }

该方法根据对象的type生成元组件或者复合组件,具体流程如下:

如果对象的type为string,则调用ReactHostComponent.createInternalComponent(element)来注入生成组件的逻辑,如果是内部元组件,则创建一个type实例, 否则,则是用户创建的复合组件,这个时候创建一个ReactCompositeComponentWrapper实例,该实例用来描述复合组件。当对象为string或者number时,调用ReactHostComponent.createInstanceForText(node)来注入组件生成逻辑。以上都不是,则报错。

我们通过前面的分析,了解了整个UI开始渲染的时机,以及js层的整个渲染流程,接下来,我们开始分析每个js的组件时怎么转换成Android的组件,最终显示在屏幕上的。

上面我们提到元组件与复合组件,事实上复合组件也是递归遍历其中的元组件,然后进行渲染。所以我们重点关注元组件的生成逻辑。

我们可以看到,UI渲染主要通过UIManager来完成,UIManager是一个ReactModule,UIManager.js里的操作都会对应到UIManagerModule里来。我们接着来看看Java层的 渲染流程。

3.2 Java层组件渲染

从上图我们可以很容易看出,Java层的组件渲染分为以下几步:

JS层通过C++层把创建View的请求发送给Java层的UIManagerModule。UIManagerModule通过UIImplentation对操作请求进行包装。包装后的操作请求被发送到View处理队列UIViewOperationQueue队列中等待处理。实际处理View时,根据class name查询对应的ViewNManager,然后调用原生View的方法对View进行相应的操作。

四 通信机制

Java层与JavaScript层的相互调用都不是直接完成的,而是间接通过C++层来完成的。在介绍通信机制之前我们先来理解一些基本的概念。

JavaScript Module注册表

说起JavaScript Module注册表,我们需要先理解3个类/接口:JavaScriptModule、JavaScriptModuleRegistration、JavaScriptModuleRegistry。

JavaScriptModule:这是一个接口,JS Module都会继承此接口,它表示在JS层会有一个相同名字的js文件,该js文件实现了该接口定义的方法,JavaScriptModuleRegistry会利用 动态代理将这个接口生成代理类,并通过C++传递给JS层,进而调用JS层的方法。JavaScriptModuleRegistration用来描述JavaScriptModule的相关信息,它利用反射获取接口里定义的Method。JavaScriptModuleRegistry:JS Module注册表,内部维护了一个HashMap:HashMap, JavaScriptModuleRegistration> mModuleRegistrations, JavaScriptModuleRegistry利用动态代理生成接口JavaScriptModule对应的代理类,再通过C++传递到JS层,从而调用JS层的方法。

Java Module注册表

要理解Java Module注册表,我们同样也需要理解3个类/接口:NativeModule、ModuleHolder、NativeModuleRegistry。

NativeModule:是一个接口,实现了该接口则可以被JS层调用,我们在为JS层提供Java API时通常会继承BaseJavaModule/ReactContextBaseJavaModule,这两个类就 实现了NativeModule接口。ModuleHolder:NativeModule的一个Holder类,可以实现NativeModule的懒加载。NativeModuleRegistry:Java Module注册表,内部持有Map:Map, ModuleHolder> mModules,NativeModuleRegistry可以遍历 并返回Java Module供调用者使用。

4.1 创建注册表

关于NativeModuleRegistry和JavaScriptModuleRegistry的创建,我们前面都已经提到管,大家还都记得吗。

NativeModuleRegistry是在createReactContext()方法里构建的。JavaScriptModuleRegistry是在CatalystInstanceImpl的构建方法里构建的。

这些都是在CatalystInstanceImpl的构建方法里通过native方法initializeBridge()传入了C++层,如下所示:

CatalystInstanceImpl.cpp

void CatalystInstanceImpl::initializeBridge( jni::alias_refReactCallback::javaobject callback, // This executor is actually a factory holder. JavaScriptExecutorHolder* jseh, jni::alias_refJavaMessageQueueThread::javaobject jsQueue, jni::alias_refJavaMessageQueueThread::javaobject moduleQueue, jni::alias_ref javaModules, jni::alias_ref cxxModules) {

instance_->initializeBridge(folly::make_unique(callback), jseh->getExecutorFactory(), folly::make_unique(jsQueue), folly::make_unique(moduleQueue), buildModuleRegistry(std::weak_ptr(instance_), javaModules, cxxModules)); }

这个方法的参数含义如下所示:

ReactCallback callback:CatalystInstanceImpl的静态内部类ReactCallback,负责接口回调。JavaScriptExecutor jsExecutor:JS执行器,将JS的调用传递给C++层。MessageQueueThread jsQueue.getJSQueueThread():JS线程,通过mReactQueueConfiguration.getJSQueueThread()获得,mReactQueueConfiguration通过ReactQueueConfigurationSpec.createDefault()创建。MessageQueueThread moduleQueue:Native线程,通过mReactQueueConfiguration.getNativeModulesQueueThread()获得,mReactQueueConfiguration通过ReactQueueConfigurationSpec.createDefault()创建。Collection javaModules:java modules,来源于mJavaRegistry.getJavaModules(this)。Collection cxxModules):c++ modules,来源于mJavaRegistry.getCxxModules()。

我们注意这些传入了两个集合:

javaModules:传入的是Collection ,JavaModuleWrapper是NativeHolder的一个Wrapper类,它对应了C++层JavaModuleWrapper.cpp, JS在Java的时候最终会调用到这个类的inovke()方法上。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)

最后:学习总结——Android框架体系架构知识脑图(纯手绘xmind文档)

学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。

下方即为我手绘的Android框架体系架构知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的Android框架体系架构知识脑图原件(包括上方的面试解析xmind文档)

除此之外,前文所提及的Alibaba珍藏版 Android框架体系架构 手写文档以及一本 《大话数据结构》 书籍等等相关的学习笔记文档,也皆可分享给认可的朋友!

——感谢大家伙的认可支持,请注意:点赞+点赞+点赞!!!

学习总结——Android框架体系架构知识脑图(纯手绘xmind文档)

学完之后,若是想验收效果如何,其实最好的方法就是可自己去总结一下。比如我就会在学习完一个东西之后自己去手绘一份xmind文件的知识梳理大纲脑图,这样也可方便后续的复习,且都是自己的理解,相信随便瞟几眼就能迅速过完整个知识,脑补回来。

下方即为我手绘的Android框架体系架构知识脑图,由于是xmind文件,不好上传,所以小编将其以图片形式导出来传在此处,细节方面不是特别清晰。但可给感兴趣的朋友提供完整的Android框架体系架构知识脑图原件(包括上方的面试解析xmind文档) [外链图片转存中…(img-UxwZgpiO-1711803093989)]

除此之外,前文所提及的Alibaba珍藏版 Android框架体系架构 手写文档以及一本 《大话数据结构》 书籍等等相关的学习笔记文档,也皆可分享给认可的朋友!

——感谢大家伙的认可支持,请注意:点赞+点赞+点赞!!!

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

参考链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: