Wait… What Happens When my React Native Application Starts? — An In-depth Look Inside React Native
Discover how React Native functions internally, and what it does for you without you knowing it.
Disclaimer: this articles assumes a (very) basic understanding of React Native and Native Modules. If you have never played around with them, I’d recommend getting a look at the official documentation first.
Edit: This article is based on a talk I’ve given in London for the ReactFestconference in March 2018. You can see me talk over here!
As I started using React Native, something quickly bugged me: what’s happening there? Indeed, from the standpoint of someone who simply uses React Native to build (great) applications, things can sometimes look magical. Want to make some native code usable from Javascript? Simply use @ReactMethod (or RCT_EXPORT_METHOD on iOS)! Want to send an event over from native to Javascript? No problem, just grab the appropriate Javascript module and call it like you would do for any native method! Not to mention, the same Javascript code will run on both iOS and Android…
As one might guess, these things are not trivial. Fairly complex, in fact. So what enables React Native to achieve such feats? The easy and commonly given answer is:
The React Native “Bridge”, duh!
In other words:
Some React Native explanations…
But who wants easy answers? This time around we will go into details. But be warned: things might get messy.
The React Native Infrastructure
Let’s get straight to the point: in order to function, React Native relies on a full infrastructure built at runtime. Ta-da!
Wait… didn’t you just give us another cryptic explanation?
I did? But there are two things to note here. First, infrastructure: the famous “bridge” is just a part of it — and not the most incredible, I dare say. Second, runtime: the above infrastructure is being built every single time you launch a React Native application, before any custom code is executed. In other words, before your application comes online and becomes visible, it goes through a transitional state where React Native is busy building your application’s foundations for you.
What does this so-called infrastructure looks like, you say? Well, let us draw a map, showing various parts (linked by arrows roughly meaning “stores a reference to”).
React Native Infrastructure — the Java logo could be replaced with an Objective-C one in the case of iOS
Woops, that got a little complex, didn’t it? And this is a simplified version… In order to understand this mess, we will describe its parts one by one, in the order in which they are created at launch. Let’s go!
Starting a React Native Application
Remember that the whole thing is built whenever a React Native application starts? Let us go through the multiple steps that separate the instant when you press your application’s logo on your phone from the moment everything becomes visible.
First, your application only has two things to work with:
- The application’s code,
 - A unique thread, the main thread* , that is automatically assigned to it by the phone’s operating system.
 
To simplify explanations, we are going to conceptually split the code in two: the framework code — the one you do not have to write every time — and the _custom code _— that actually describes your application. Both being distributed over Javascript and native, this gives us a total of four parts of code to go through. The first thing that will get processed — on the main thread — is the native part of the framework code.
*also called UI Thread_, as — outside of initialization — it will mainly be responsible of UI-related work_
Creating the Native Foundations
One important thing to realize is that, although most of the UI code — <View>, <Text>… — is written in Javascript, what will in the end be rendered are only native views. That’s it. This means that the React Native framework needs to:
- create native views and map their connections to Javascript components,
 - store these natives views and display them.
 
While the first step will be handled by the UIManagerModule (which we will describe a little later), the RootView will take care of the second one. The RootView is more or less a container in which native views are organized in a big tree — a native representation of the Javascript component tree, if you like — and everything that will show up on the phone’s screen will be stored in there.
Back to our initializing process: everything starts with the creation of the above RootView— an empty container for now — before moving on to the Bridge Interface.
Wasn’t the bridge supposed to be between native and Javascript? Why would you need an interface there?
It is! But although most of the native side — including the RootView — is written in a platform-specific language (Objective-C or Java), the bridge is entirely implemented in C++ . The Bridge Interface therefore acts as an API, allowing the former to interact with the latter. The bridge itself is made up of two ends, native to Javascript, and vice versa.
The bridge, however, would be nothing without endpoints to dispatch calls to. These endpoints are Native Modules and will, in the end, be the only things available to the Javascript environment. In other words, everything but Native Modules will be eventually invisible to your Javascript application. For this reason, aside from the Custom Modules that you may or may not decide to create, the framework also includes Core Modules. One example of the latter would be the UIManagerModule , which stores a map of all Javascript UI Components and their associated native views. Every time a Javascript UI Component is created, updated or deleted, the UIManagerModule will use this map to accordingly create, update or delete the corresponding native view. It will also forward changes to the native view tree stored in the RootView in order to make them visible.
From the standpoint of initialization, all Native Modules are treated the same: for each module, an instance is created, and a reference to that instance is stored on the Javascript to native bridge — so that they can later be called from Javascript. In addition, a reference to the Bridge Interface is also passed to each Native Module, allowing them to call Javascript directly. Finally, two additional threads will be created: the JS Thread and the NativeModulesThread*.
*strictly speaking, it is not a unique thread but a pool of threads in the case of the iOS implementation of React Native.
Intermezzo: Setting Up the Javascript Engine
Before moving on, let’s make a quick summary of what has happened so far:
- a bunch of native stuff has been created on the main thread,
 - we now have three threads to work with,
 - absolutely no Javascript has been processed yet.
 
Referring back to our initial map, what we have is this:
React Native’s native side
Meaning it is now time to load the Javascript bundle — framework and custom code alike!
Being an interpreted scripting language, Javascript cannot be run as is: it needs to be converted to bytecode, then be executed. This is the job of the Javascript virtual machine (a.k.a. Javascript engine). There are many Javascript engines out there, including Chrome’s V8, Mozilla’s SpiderMonkey and Safari’s JavaScriptCore… If in debug mode, React Native will use V8 and directly run in the browser, otherwise, it defaults to JavaScriptCore and runs on the device. As a side note, JavaScriptCore is not included in Android by default (while it is in iOS), so React Native automatically bundles a copy of it in the Android application — making Android applications slightly heavier than their iOS counterparts.
In any case, before effectively kicking off the Javascript engine, React Native has to give it a Context representing the execution environment. That includes the Javascript global object, and means that this global object is in fact created and stored on the C++ bridge. Why is that so important? Because the global object is then not only reachable from within the Javascript environment, but also from outside. It is therefore the primary means of communication between C++ (native) and Javascript, as it is through the global object that some native functions will be made available to Javascript — functions that will in turn be used to pass back data from Javascript to native.
Many things are stored on the global object, but the ModuleConfig array and the flushQueue() function are especially important. Each element of the ModuleConfig array describes a Native Module (be it Core or Custom), including its name, its exported constants, methods… The flushQueue()function plays a critical role in assuring communication between the Javascript and native environment, as it it will be used periodically to pass calls from the first back to the second.
Once the Javascript Context has been fully created and filled, it is fed to the Javascript engine that starts loading the React Native Javascript bundle on theJS Thread.
Loading the Javascript Bundle
As the virtual machine begins processing the Javascript part of the framework code, it will create the BatchedBridge. That name might ring a bell as it sometimes pops up in error messages! Despite its fancy denomination, it is but a simple queue, that stores “calls from Javascript to native”. A “call” is an object containing a native module ID, a method ID (for the specified native module), along with arguments that the native method is to be called with. Periodically (every 5 milliseconds by default), the BatchedBridge will call on global.flushQueue(), passing its content — an array of “calls” — to the Javascript to native end of the C++ bridge. Know as batches , these small arrays are indexed so as to ensure that all UI changes contained in one batch are made visible at the same time (this is necessary because the entire process is asynchronous). The Javascript to native end of the bridge will finally iterate over each call in a batch and dispatch them to the appropriate native module using the specified module ID — which it can do because it has a reference pointing to each and any native module, remember?
The next step is creating the NativeModules object — yes, the very object that has to be imported from ‘react-native’ each time you want to call a native module. The NativeModules object will be filled using the ModuleConfigarray mentioned earlier. I will not go into the details of this process here, but it is roughly equivalent to doing NativeModules[module_name]={} for each module_name contained in the ModuleConfig, then NativeModules[module_name][method_name]=fillerMethod for each exported native method of the given module. fillerMethod is simply there to store all arguments it receives on the BatchedBridge , along with the method and module ID (something like fillerMethod = function(...args) { BatchedBridge.enqueueNativeCall(moduleID, methodID, args)} ), effectively creating a “call” from Javascript to native. That being said, what is fired when you later write MyNativeModule.myMethod(args) is actually the abovefillerMethod !
We’re almost there. The last thing that needs to be done is creating the core JS Modules, among which the DeviceEventEmitter— that will be used to send events from native to Javascript — or the AppRegistry, that stores a reference to the main components of your app. In order to be callable from native, these modules are registered on the Javascript global object…
…and with that, the full React Native infrastructure has been built!
Making the React Native Application Visible
Despite the initialization being all but complete, our application is still invisible at this stage! Indeed, the loading of the Javascript bundle happened on the JS thread, which is independent of the main (a.k.a. UI) thread. The JS thread thus has to warn the main thread about the completion of its task, and in response, the main thread uses the AppRegistry (JS module) to ask the JS thread to process the main custom component — usually App.js .
To sum it up from a threading perspective, a React Native application’s launch process looks like this:
React Native’s starting routine
The Javascript component tree contained in your application’s main component will be traversed, calling the UIManagerModule every time a UI component is encountered. The UIManagerModule (on the UI thread) will in turn take care of creating native views and storing them in the RootView : congratulations, your application is now visible! 												
Wait… What Happens When my React Native Application Starts? — An In-depth Look Inside React Native的更多相关文章
- Native Application 开发详解(直接在程序中调用 ntdll.dll 中的 Native API,有内存小、速度快、安全、API丰富等8大优点)
		
文章目录: 1. 引子: 2. Native Application Demo 展示: 3. Native Application 简介: 4. Native Ap ...
 - 微软推出了Cloud Native Application Bundles和开源ONNX Runtime
		
微软的Microsoft Connect(); 2018年的开发者大会 对Azure和IoT Edge服务进行了大量更新; Windows Presentation Foundation,Window ...
 - react系列笔记1 用npx npm命令创建react app
		
react系列笔记1 用npx npm命令创建react app create-react-app my-app是开始构建新的 React 单页应用程序的最佳方式.它已经为你设置好了开发环境,以便您可 ...
 - 关于React.PropTypes的废除,以及新版本下的react的验证方式
		
React.PropTypes是React用来typechecking的一个属性.要在组件的props上运行typechecking,可以分配特殊的propTypes属性: class Greetin ...
 - react全家桶从0搭建一个完整的react项目(react-router4、redux、redux-saga)
		
react全家桶从0到1(最新) 本文从零开始,逐步讲解如何用react全家桶搭建一个完整的react项目.文中针对react.webpack.babel.react-route.redux.redu ...
 - React实战教程之从零开始手把手教你使用 React 最新特性Hooks API 打造一款计算机知识测验App
		
项目演示地址 项目演示地址 项目代码结构 前言 React 框架的优雅不言而喻,组件化的编程思想使得React框架开发的项目代码简洁,易懂,但早期 React 类组件的写法略显繁琐.React Hoo ...
 - React Native 开发之 (02) 用Sublime 3作为React Native的开发IDE
		
Sublime Text是一个代码编辑器.也是HTML和散文先进的文本编辑器.漂亮的用户界面和非凡的功能,例如:迷你地图,多选择Python插件,代码段等等.完全可自定义键绑定,菜单和工具栏等等.漂亮 ...
 - How to debug Android Native Application with eclipse
		
This blog is inspired by this tutorial http://mhandroid.wordpress.com/2011/01/23/using-eclipse-for-a ...
 - [React] Configure a React & Redux Application For Production Deployment and Deploy to Now
		
In this lesson, we’ll make a few small changes to our scripts and add some environment variables tha ...
 
随机推荐
- centos6.8下hadoop3.1.1完全分布式安装指南
			
前述:这篇文档是建立在三台虚拟机相互ping通,防火墙关闭,hosts文件修改,SSH 免密码登录,主机名修改等的基础上开始的. 一.传入文件 1.创建安装目录 mkdir /usr/local/so ...
 - Linux命令——ethtool
			
转自:https://www.cnblogs.com/kelamoyujuzhen/p/10116423.html 参考:9 Linux ethtool Examples to Manipulate ...
 - xenserver 添加和卸载硬盘
			
最近在浪潮服务器上安了xenserver系统,创建虚拟机,没注意磁盘超负载就重启了服务导致各种坑,一言难尽,忧伤逆流成河啊,所以准备将各种操作整理总结记录下,持续更新ing~~ ...
 - 很不错的python 机器学习资源
			
http://www.cuijiahua.com/resource.html 曾看过的书,感觉一些很有用的学习资料,推荐给大家! Python基础: 网络教程推荐: 系统学习python3可以看廖雪峰 ...
 - PoI 3.17 已过时代码对比
			
PoI 3.17 已过时代码对比颜色定义变化旧版本 : HSSFColor.BLACK.index新版本 : IndexedColors.BLACK.index 获取单元格格式旧版本 : cell.g ...
 - win10系统易升更新不成功c盘也满了,解决方法
			
删除临时更新文件: 1)同时按下Windows键和R键,打开运行,输入services.msc 2)找到WindowsUpdate服务项,右键选择禁用. 3)打开c:\windows\Software ...
 - JavaEE 项目部署方式
			
一.手动部署 二.自动部署 “自动化”的具体体现:向版本库提交新的代码后,应用服务器上自动部署,用户或测试人员使用的马上就是最新的应用程序. 搭建上述持续集成环境可以把整个构建.部署过程自动化,很大程 ...
 - NLP之词向量
			
1.对词用独热编码进行表示的缺点 向量的维度会随着句子中词的类型的增大而增大,最后可能会造成维度灾难2.任意两个词之间都是孤立的,仅仅将词符号化,不包含任何语义信息,根本无法表示出在语义层面上词与词之 ...
 - [LeetCode] 419. Battleships in a Board 平板上的战船
			
Given an 2D board, count how many battleships are in it. The battleships are represented with 'X's, ...
 - [LeetCode] 137. Single Number II 单独的数字之二
			
Given a non-empty array of integers, every element appears three times except for one, which appears ...