native react 源码修改_修改react native源码 - CSDN
  • 如果按官方指导走来修改安卓源码然后从源码编译,可以要人老命,各种环境配置,麻烦程度堪称杀人利器。 本文将另辟稀径,解决这一麻烦。 一、新建一个java工程,最好是maven项目,然后将项目下面的node_modules/...

    如果按官方指导走来修改安卓源码然后从源码编译,可以要人老命,各种环境配置,麻烦程度堪称杀人利器。

    本文将另辟稀径,解决这一麻烦。

     

    一、新建一个java工程,最好是maven项目,然后将自己react native项目下面的node_modules/react-native/ReactAndroid/src/main/java里面的java源码复制到新的工程;

    二、从自己react native项目下面的node_modules\react-native\android\com\facebook\react\react-native\0.60.5\react-native-0.60.5.pom里,将maven依赖复制到新的工程里面的pom.xml中。然后对依赖要作如下的调整:

    1.有些依赖是位于google仓库的,是访问不了的,可以下载下来通过本地的方式添加到构建目录,如android相关的一些依赖;

    2.有些依赖是aar格式,不再是传统的jar了,要特别注意;

    三、按需要修改源码;

    四、将修改过的类的编译过的字节码通过7-zip工具拖入到自己react native项目下面的node_modules/react-native/android/com/facebook/react/react-native/0.60.5/react-native-0.60.5.aar文件里面的classes.jar中的对应位置即可;

     

     

     

    展开全文
  • 本篇前两部分内容简单介绍一下ReactNative,后面的章节会把整个RN框架的iOS部分,进行代码层面的一一梳理 全文是不是有点太长了,我要不要分拆成几篇文章 函数栈代码流程图,由于采用层次缩进的形式,层次关系比较...

    本篇前两部分内容简单介绍一下ReactNative,后面的章节会把整个RN框架的iOS部分,进行代码层面的一一梳理

    全文是不是有点太长了,我要不要分拆成几篇文章

    函数栈代码流程图,由于采用层次缩进的形式,层次关系比较深的话,不是很利于手机阅读,

    ReactNative 概要

    ReactNative,动态,跨平台,热更新,这几个词现在越来越火了,一句使用JavaScript写源生App吸引力了无数人的眼球,并且诞生了这么久也逐渐趋于稳定,携程,天猫,QZone也都在大产品线的业务上,部分模块采用这个方案上线,并且效果得到了验证(见2016 GMTC 资料PPT)

    我们把这个单词拆解成2部分

    • React

    熟悉前端的朋友们可能都知道React.JS这个前端框架,没错整个RN框架的JS代码部分,就是React.JS,所有这个框架的特点,完完全全都可以在RN里面使用(这里还融入了Flux,很好的把传统的MVC重组为dispatch,store和components,Flux架构

    所以说,写RN哪不懂了,去翻React.JS的文档或许都能给你解答

    以上由@彩虹 帮忙修正

    • Native

    顾名思义,纯源生的native体验,纯源生的UI组件,纯原生的触摸响应,纯源生的模块功能

    那么这两个不相干的东西是如何关联在一起的呢?

    React.JS是一个前端框架,在浏览器内H5开发上被广泛使用,他在渲染render()这个环节,在经过各种flexbox布局算法之后,要在确定的位置去绘制这个界面元素的时候,需要通过浏览器去实现。他在响应触摸touchEvent()这个环节,依然是需要浏览器去捕获用户的触摸行为,然后回调React.JS

    上面提到的都是纯网页,纯H5,但如果我们把render()这个事情拦截下来,不走浏览器,而是走native会怎样呢?

    当React.JS已经计算完每个页面元素的位置大小,本来要传给浏览器,让浏览器进行渲染,这时候我们不传给浏览器了,而是通过一个JS/OC的桥梁,去通过[[UIView alloc]initWithFrame:frame]的OC代码,把这个界面元素渲染了,那我们就相当于用React.JS绘制出了一个native的View

    拿我们刚刚绘制出得native的View,当他发生native源生的- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event触摸事件的时候,通过一个OC/JS的桥梁,去调用React.JS里面写好的点击事件JS代码

    这样React.JS还是那个React.JS,他的使用方法没发生变化,但是却获得了纯源生native的体验,native的组件渲染,native的触摸响应

    于是,这个东西就叫做React-Native

    ReactNative 结构

    大家可以看到,刚才我说的核心就是一个桥梁,无论是JS=>OC,还是OC=>JS。

    刚才举得例子,就相当于把纯源生的UI模块,接入这个桥梁,从而让源生UI与React.JS融为一体。

    那我们把野心放长远点,我们不止想让React.JS操作UI,我还想用JS操作数据库!无论是新玩意Realm,还是老玩意CoreData,FMDB,我都希望能用JS操作应该怎么办?好办,把纯源生的DB代码模块,接入这个桥梁

    如果我想让JS操作Socket做长连接呢?好办,把源生socket代码模块接入这个桥梁。如果我想让JS能操作支付宝,微信,苹果IAP呢?好办,把源生支付代码模块接入这个桥梁

    由此可见RN就是由一个bridge桥梁,连接起了JS与na的代码模块

    • 链接了哪个模块,哪个模块就能用JS来操作,就能动态更新
    • 发现现有RN框架有些功能做不到了?扩展写个na代码模块,接入这个桥梁

    这是一个极度模块化可扩展的桥梁框架,不是说你从facebook的源上拉下来RN的代码,RN的能力就固定一成不变了,他的模块化可扩展,让你缺啥补上啥就好了

    ReactNative 结构图

    RN结构图

    大家可以看这个结构图,整个RN的结构分为四个部分,上面提到的,RN桥的模块化可扩展性,就体现在JSBridge/OCBridge里的ModuleConfig,只要遵循RN的协议RCTBridgeModule去写的OC Module对象,使用RCT_EXPORT_MODULE()宏注册类,使用RCT_EXPORT_METHOD()宏注册方法,那么这个OC Module以及他的OC Method都会被JS与OC的ModuleConfig进行统一控制

    RN类图

    上面是RN的代码类结构图

    • 大家可以看到RCTRootView是RN的根试图,

      • 他内部持有了一个RCTBridge,但是这个RCTBridge并没有太多的代码,而是持有了另一个RCTBatchBridge对象,大部分的业务逻辑都转发给BatchBridge,BatchBridge里面写着的大量的核心代码

        • BatchBridge会通过RCTJavaScriptLoader来加载JSBundle,在加载完毕后,这个loader也没什么太大的用了

        • BatchBridge会持有一个RCTDisplayLink,这个对象主要用于一些Timer,Navigator的Module需要按着屏幕渲染频率回调JS用的,只是给部分Module需求使用

        • RCTModuleXX所有的RN的Module组件都是RCTModuleData,无论是RN的核心系统组件,还是扩展的UI组件,API组件

          • RCTJSExecutor是一个很特殊的RCTModuleData,虽然他被当做组件module一起管理,统一注册,但他是系统组件的核心之一,他负责单独开一个线程,执行JS代码,处理JS回调,是bridge的核心通道
          • RCTEventDispatcher也是一个很特殊的RCTModuleData,虽然他被当做组件module一起管理,统一注册,但是他负责的是各个业务模块通过他主动发起调用js,比如UIModule,发生了点击事件,是通过他主动回调JS的,他回调JS也是通过RCTJSExecutor来操作,他的作用是封装了eventDispatcher得API来方便业务Module使用。

    后面我会详细按着代码执行的流程给大家细化OCCode里面的代码,JSCode由于我对前端理解还不太深入,这个Blog就不会去拆解分析JS代码了

    ReactNative通信机制可以参考bang哥的博客 React Native通信机制详解

    ReactNative 初始化代码分析

    我会按着函数调用栈类似的形式梳理出一个代码流程表,对每一个调用环节进行简单标记与作用说明,在整个表梳理完毕后,我会一一把每个标记进行详细的源码分析和解释

    下面的代码流程表,如果有类名+方法的,你可以直接在RN源码中定位到具体代码段

    • RCTRootView-initWithBundleURLXXX(RootInit标记)
      • RCTBridge-initWithBundleXXX
        • RCTBridge-createBatchedBridge(BatchBridgeInit标记
          • New Displaylink(DisplaylinkInit标记
          • New dispatchQueue (dispatchQueueInit标记)
          • New dispatchGroup (dispatchGroupInit标记)
          • group Enter(groupEnterLoadSource标记
            • RCTBatchedBridge-loadSource (loadJS标记)
          • RCTBatchedBridge-initModulesWithDispatchGroup(InitModule标记 这块内容非常多,有个子代码流程表)
          • group Enter(groupEnterJSConfig标记
            • RCTBatchedBridge-setUpExecutor(configJSExecutor标记
            • RCTBatchedBridge-moduleConfig(moduleConfig标记
            • RCTBatchedBridge-injectJSONConfiguration(moduleConfigInject标记
          • group Notify(groupDone标记
            • RCTBatchedBridge-executeSourceCode(evaluateJS标记
            • RCTDisplayLink-addToRunLoop(addrunloop标记

    RootInit标记:所有RN都是通过init方法创建的不再赘述,URL可以是网络url,也可以是本地filepath转成URL

    BatchBridgeInit标记:前边说过rootview会先持有一个RCTBridge,所有的module都是直接操作bridge所提供的接口,但是这个bridge基本上不干什么核心逻辑代码,他内部持有了一个batchbrdige,各种调用都是直接转发给RCTBatchBrdige来操作,因此batchbridge才是核心

    RCTBridge在init的时候调用[self setUp]

    RCTBridge在setUp的时候调用[self createBatchedBridge]

    DisplaylinkInit标记:batchbridge会首先初始化一个RCTDisplayLink这个东西在业务逻辑上不会被所有的module调用,他的作用是以设备屏幕渲染的频率触发一个timer,判断是否有个别module需要按着timer去回调js,如果没有module,这个模块其实就是空跑一个displaylink,注意,此时只是初始化,并没有run这个displaylink

    dispatchQueueInit标记:会初始化一个GCDqueue,后面很多操作都会被扔到这个队列里,以保证顺序执行

    dispatchGroupInit标记:后面接下来进行的一些列操作,都会被添加到这个GCDgroup之中,那些被我做了group Enter标记的,当group内所有事情做完之后,会触发group Notify

    groupEnterLoadSource标记:会把无论是从网络还是从本地,拉取jsbundle这个操作,放进GCDgroup之中,这样只有这个操作进行完了(还有其他group内操作执行完了,才会执行notify的任务)

    loadJS标记:其实就是异步去拉取jsbundle,无论是本地读还是网络啦,[RCTJavaScriptLoader loadBundleAtURL:self.bundleURL onComplete:onSourceLoad];只有当回调完成之后会执行dispatch_group_leave,离开group

    InitModule标记:这个函数是在主线程被执行的,但是刚才生成的GCD group会被当做参数传进内部,因为内部的一些逻辑是需要加入group的,这个函数内部很复杂 我会继续绘制一个代码流程表

    • 1)RCTGetModuleClasses() 

    一个C函数,RCT_EXPORT_MODULE()注册宏会在+load时候把Module类都统一管理在一个static NSArray里,通过RCTGetModuleClasses()可以取出来所有的Module

    • 2)RCTModuleData-initWithModuleClass 

    此处是一个for循环,循环刚才拿到的array,对每一个注册了得module都循环生成RCTModuleData实例

    • 3)配置moduleConfig

    每一个module在循环生成结束后,bridge会统一存储3分配置表,包含了所有的moduleConfig的信息,便于查找和管理

    
     
    
     
    • 4)RCTModuleData-instance

    这是一个for循环,每一个RCTModuleData都需要循环instance一下,需要说明的是,RCTModuleData与Module不是一个东西,各类Module继承自NSObject,RCTModuleData内部持有的instance实例才是各类Module,因此这个环节是初始化RCTModuleData真正各类Module实例的环节

    通过RCTModuleData-setUpInstanceAndBridge来初始化创建真正的Module

    
     
    
     

    这里需要说明,每一个Module都会创建一个自己独有的专属的串行GCD queue,每次js抛出来的各个module的通信,都是dispatch_async,不一定从哪个线程抛出来,但可以保证每个module内的通信事件是串行顺序的

    每一个module都有个bridge属性指向,rootview的bridge,方便快速调用

    • 5)RCTJSCExecutor 

    RCTJSCExecutor是一个特殊的module,是核心,所以这里会单独处理,生成,初始化,并且被bridge持有,方便直接调用

    RCTJSCExecutor初始化做了很多事情,需要大家仔细关注一下

    创建了一个全新的NSThread,并且被持有住,绑定了一个runloop,保证这个线程不会消失,一直在loop,所有与JS的通信,一定都通过RCTJSCExecutor来进行,所以一定是在这个NSThread线程内,只不过各个模块的消息,会进行二次分发,不一定在此线程内

    • 6)RCTModuleData-gatherConstants

    每一个module都有自己的提供给js的接口配置表,这个方法就是读取这个配置表,注意!这行代码执行在主线程,但他使用dispatch_async 到mainQueue上,说明他先放过了之前的函数调用栈,等之前的函数调用栈走完,然后还是在主线程执行这个循环的gatherConstants,因此之前传进来的GCD group派上了用场,因为只有当所有module配置都读取并配置完毕后才可以进行 run js代码

    下面思路从子代码流程表跳出,回到大代码流程表的标记

    groupEnterJSConfig标记:代码到了这块会用到刚才创建,但一直没使用的GCD queue,并且这块还比较复杂,在这次enter group内部,又创建了一个子group,都放在这个GCD queue里执行

    如果觉得绕可以这么理解他会在专属的队列里执行2件事情(后面要说的2各标记),当这2个事情执行完后触发子group notify,执行第三件事情(后面要说的第三个标记),当第三个事情执行完后leave母group,触发母group notify

    
     
    
     

    configJSExecutor标记:再次专门处理一些JSExecutor这个RCTModuleData

    1)property context懒加载,创建了一个JSContext

    2)为JSContext设置了一大堆基础block回调,都是一些RN底层的回调方法

    moduleConfig标记:把刚才所有配置moduleConfig信息汇总成一个string,包括moduleID,moduleName,moduleExport接口等等

    moduleConfigInject标记:把刚才的moduleConfig配置信息string,通过RCTJSExecutor,在他内部的专属Thread内,注入到JS环境JSContext里,完成了配置表传给JS环境的工作

    groupDone标记:GCD group内所有的工作都已完成,loadjs完毕,配置module完毕,配置JSExecutor完毕,可以放心的执行JS代码了

    evaluateJS标记:通过[_javaScriptExecutor executeApplicationScript:script sourceURL:url onComplete:]来在JSExecutor专属的Thread内执行jsbundle代码

    addrunloop标记:最早创建的RCTDisplayLink一直都只是创建完毕,但并没有运作,此时把这个displaylink绑在JSExecutor的Thread所在的runloop上,这样displaylink开始运作

    小结

    整个RN在bridge上面,单说OC侧,各种GCD,线程,队列,displaylink,还是挺复杂的,针对各个module也都是有不同的处理,把这块梳理清楚能让我们更加清楚OC代码里面,RN的线程控制,更方便以后我们扩展编写更复杂的module模块,处理更多native的线程工作。

    后面的 js call oc oc call js 我也会以同样的方式进行梳理,让大家清楚线程上是如何运作的

    PS:JS代码侧其实bridge的设计也有一套,包括所有call oc messageQueue会有个队列控制之类的,我对JS不是那么熟悉和理解,JS侧的代码我就不梳理了。

    ReactNative JS call OC 代码分析

    既然整个RCTRootView都初始化完毕,并且执行了jsbundle文件了,整个RN就已经运作起来了,那么RN运作起来后,JS的消息通过JS代码的bridge发送出来之后,是如何被OC代码识别,分发,最重转向各个module模块的业务代码呢?我们接下来就会梳理,这个流程的代码

    JS call OC 可以有很多个方法,但是所有的方法一定会走到同一个函数内,这个关键函数就是

    - (void)handleBuffer:(id)buffer batchEnded:(BOOL)batchEnded

    需要说明的事,handleBuffer一定发生在RCTJSExecutor的Thread内

    正所谓顺藤摸瓜,我可以顺着他往上摸看看都哪里会发起js2oc的通信

    • [RCTJSExecutor setUp]

    可以看到这里面有很多JavaScriptCore的JSContext[“xxx”]=block的用法,这个用法就是JS可以把xxx当做js里面可以识别的function,object,来直接调用,从而调用到block得意思,可以看出来nativeFlushQueueImmediate当js主动调用这个jsfunction的时候,就会下发一下数据,从而调用handleBuffer,可以确定的是,这个jsfunction,会在jsbunlde run起来后立刻执行一次

    这个方法要特别强调一下,这是唯一个一个JS会主动调用OC的方法,其他的js调用OC,都他由OC实现传给JS一个回调,让JS调用。

    JS侧主动调用nativeFlushQueueImmediate的逻辑

    • [RCTBatchBridge enqueueApplicationScript:]

    可以看到这句代码只发生在执行jsbundle之后,执行之后会[RCTJSExecutor flushedQueue:callback]在callback里调用handleBuffer,说明刚刚执行完jsbundle后会由OC主动发起一次flushedQueue,并且传给js一个回调,js通过这个回调,会call oc,进入handleBuffer

    • [RCTBatchBridge _actuallyInvokeCallback:]
    • [RCTBatchBridge _actuallyInvokeAndProcessModule:]

    两个_actuallyInvoke开头的方法,用处都是OC主动发起调用js的时候,会传入一个call back block,js通过这个callback block回调,这两个方法最后都会执行[RCTJSExecutor _executeJSCall:]

    从上面可以看出JS只有一个主动调用OC的方法,其他都是通过OC主动调用JS给予的回调

    我们还可以顺着handleBuffer往下摸看看都会如何分发JS call OC的事件

    以handleBuffer为根,我们继续用函数站代码流程表来梳理

    • RCTBatchedBridge-handlebuffer
      • analyze Buffer(analyze buffer标记)
      • find module(find modules标记
      • for 循环all calls
      • dispatch async(dispatch async标记
        • [RCTBatchedBridge- handleRequestNumber:]
          • [RCTBridgeMethod invokeWithBridge:](invocation标记 这个标记会复杂点,子流程表细说) 

    analyze buffer标记:js传过来的buffer其实是一串calls的数组,一次性发过来好几个消息,需要OC处理,所以会解析buffer,分别识别出每一个call的module信息

    
     
    
     

    find modules标记:解析了buffer之后就要查找对应的module,不仅要找到RCTModuleData,同时还要取出RCTModuleData自己专属的串行GCD queue

    dispatch async标记:每一个module和queue都找到了就可以for循环了,去执行一段代码,尤其要注意,此处RN的处理是直接dispatch_async到系统随机某一个空闲线程,因为有模块专属queue的控制,还是可以保持不同模块内消息顺序的可控

    invocation标记:这个标记的作用就是真真正正的去调用并且执行对应module模块的native代码了,也就是JS最终调用了OC,这个标记内部还比较复杂,里面使用了NSInvocation去运行时查找module类进行反射调用

    invocation内部子流程如下

    解释一下,JS传给OC是可以把JS的回调当做参数一并传过来的,所以后面的流程中会特别梳理一下这种回调参数是如何实现的,

    • [RCTBridgeMethod-processMethodSignature](invocation预处理标记
      • argumentBlocks(参数处理标记
    • 循环压参(invocation压参标记
    • 反射执行Invocation调用oc

    invocation预处理标记:RN会提前把即将反射调用的selector进行分析,分析有几个参数每个参数都是什么类型,每种类型是否需要包装或者转化处理。 

    参数处理标记:argumentBlocks其实是包装或转化处理的block函数,每种参数都有自己专属的block,根据类型进行不同的包装转化策略

    此处别的参数处理不细说了,单说一下JS回调的这种参数是怎么操作的

    • JS回调通过bridge传过来的其实是一个数字,是js回调function的id
    • 我们在开发module的时候,RN让你声明JS回调的时候是声明一个输入参数为NSArray的block
    • js回调型参数的argumentBlocks的作用就是,把jsfunctionid进行记录,包装生成一个输入参数为NSArray的block,这个block会自动的调用[RCTBridge enqueueCallback:]在需要的时候回调JS,然后把这个block压入参数,等待传给module

    这块代码各种宏嵌套,还真是挺绕的,因为宏的形式,可读性非常之差,但是读完了后还是会觉得很风骚

    [RCTBridgeMethod processMethodSignature]这个方法,强烈推荐

    invocation压参标记:argumentBlocks可以理解为预处理专门准备的处理每个参数的函数,那么预处理结束后,就该循环调用argumentBlocks把每一个参数处理一下,然后压入invocation了

    后面就会直接调用到你写的业务模块的代码了,业务模块通过那个callback回调也能直接calljs了

    ReactNative OC call JS EventDispatcher代码分析

    我们编写module,纯源生native模块的时候,有时候会有主动要call js的需求,而不是通过js给的callback calljs

    这时候就需要RCTEventDispatcher了,可以看到他的头文件里都是各种sendEvent,sendXXXX的封装,看一下具体实现就会发现,无论是怎么封装,最后都走到了[RCTJSExecutor enqueueJSCall:],追中还是通过RCTJSExecutor,主动发起调用了JS

    他有两种方式

    • 直接立刻发送消息主动callJS
    • 把消息add进一个Event队列,然后通过flushEventsQueue一次性主动callJS

    ReactNative Displaylink 代码分析

    之前我们提到过一个RCTDisplayLink,没错他被添加到RCTJSExecutor的Thread所在的runloop之上,以渲染频率触发执行代码,执行frameupDate

    [RCTDisplaylink _jsThreadUpdate]

    在这个方法里,会拉取所有的需要执行frameUpdate的module,在module所在的队列里面dispatch_async执行didUpdateFrame方法

    在各自模块的didUpdateFrame方法内,会有自己的业务逻辑,以DisplayLink的频率,主动call js

    比如:RCTTimer模块

    RCTJSExecutor

    最后在强调下JSBridge这个管道的线程控制的情况

    刚才提到的无论是OC Call JS还是JS call OC,都只是在梳理代码流程,让你清楚,所有JS/OC之间的通信,都是通过RCTJSExecutor,都是在RCTJSExecutor内部所在的Thread里面进行

    如果发起调用方OC,并不是在JSThread执行,RCTJSExecutor就会把代码perform到JSThread去执行

    发起调用方是JS的话,所有JS都是在JSThread执行,所以handleBuffer也是在JSThread执行,只是在最终分发给各个module的时候,才进行了async+queue的控制分发。

    展开全文
  • 尝试将ReactNative源码编译并在Android手机上运行一个HelloWorld, 浪费不少时间踩坑,总结一下整个搭建过程:   1. 准备工作: Android SDK 26.0.1 Android NDK android-ndk-r...

    尝试将ReactNative源码编译并在Android手机上运行一个HelloWorld, 浪费不少时间踩坑,总结一下整个搭建过程:

     

    1. 准备工作:

    Android SDK                                      26.0.1

    Android NDK                                      android-ndk-r10e

    React Native Source Code:               0.54.4

    ReactNativeTool(react-native):          2.0.1

    (1) 创建目录:

     

    C:\code\ReactNative\sourcecode2    放置RN源码

    C:\code\ReactNative\RNDemoApp   放置RN HelloWorld js app 目录

    C:\code\ReactNativeDemo\               Android ReactNativeDemo程序

     

    (2) Android NDK下载:略

    必须用android-ndk-r10e,高版本编译C++过程会报错。

     

    (3) Android SDK下载:略

     

    (4) React Native Source Code 下载安装:

    $ cd /c/code/ReactNative/sourcecode2

    $ npm install --save react-native@0.54.4

     

    (5) ReactNativeTool(react-native)下载安装:

    $ cd /c/code/ReactNative

     

    $ npm install -g yarnreact-native-cli

     

     

    2. AndroidStudio向导创建一个名字为ReactNativeDemo的Android工程.

    选择Minimum SDK API 16, 选择Empty Activity,其它默认.

     

    3. ReactNativeDemo相关文件修改 

    C:\code\ReactNativeDemo\settings.gradle

    include ':app', ':ReactAndroid'
    
    project(':ReactAndroid').projectDir = new File(rootProject.projectDir, '../ReactNative/sourcecode2/node_modules/react-native/ReactAndroid')

     

    C:\code\ReactNativeDemo\local.properties

    #ndk.dir=C\:\\Users\\Win7\\AppData\\Local\\Android\\Sdk\\ndk-bundle
    ndk.dir=C\:\\Users\\Win7\\AppData\\Local\\Android\\Sdk\\android-ndk-r10e
    sdk.dir=C\:\\Users\\Win7\\AppData\\Local\\Android\\Sdk

     

    C:\code\ReactNativeDemo\build.gradle

    buildscript {
        repositories {
            jcenter()
        }
        dependencies {
            classpath 'com.android.tools.build:gradle:2.3.3'
            classpath 'de.undercouch:gradle-download-task:3.1.2'
            // NOTE: Do not place your application dependencies here; they belong
            // in the individual module build.gradle files
        }
    }
    
    allprojects {
        repositories {
            jcenter()
            maven {
                url "https://maven.google.com"
            }
        }
    }
    
     

    C:\code\ReactNativeDemo\app\build.gradle

    android {
        compileSdkVersion 26
        buildToolsVersion "26.0.1"
        defaultConfig {
            applicationId "com.bzsample.reactnativedemo"
            minSdkVersion 16
            targetSdkVersion 26
            versionCode 1
            versionName "1.0"
            ndk{
                abiFilters "armeabi-v7a","x86"
            }
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    
    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
            exclude group: 'com.android.support', module: 'support-annotations'
        })
        compile 'com.android.support:appcompat-v7:26.+'
        compile project(':ReactAndroid')
        compile 'com.android.support.constraint:constraint-layout:1.0.2'
    
        testCompile 'junit:junit:4.12'
    }

     

    C:\code\ReactNativeDemo\app\src\main\AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.bzsample.reactnativedemo">
    
        <uses-permission android:name="android.permission.INTERNET" />
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.AppCompat.Light.NoActionBar" >
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>

     

    C:\code\ReactNativeDemo\app\src\main\java\com\bzsample\reactnativedemo\MainActivity.java 

    package com.bzsample.reactnativedemo;
    
    import android.content.Intent;
    import android.net.Uri;
    import android.os.Build;
    import android.provider.Settings;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.KeyEvent;
    
    import com.facebook.react.ReactInstanceManager;
    import com.facebook.react.ReactRootView;
    import com.facebook.react.common.LifecycleState;
    import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
    import com.facebook.react.shell.MainReactPackage;
    
    public class MainActivity extends AppCompatActivity implements DefaultHardwareBackBtnHandler {
    
        private ReactRootView mReactRootView;
        private ReactInstanceManager mReactInstanceManager;
        private static final int OVERLAY_PERMISSION_REQ_CODE = 100;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mReactRootView = new ReactRootView(this);
            mReactInstanceManager = ReactInstanceManager.builder()
                    .setApplication(getApplication())
                    .setBundleAssetName("index.android.bundle")
                    .setJSMainModulePath("index.android")
                    .addPackage(new MainReactPackage())
                    .setUseDeveloperSupport(BuildConfig.DEBUG)
                    .setInitialLifecycleState(LifecycleState.RESUMED)
                    .build();
            
            mReactRootView.startReactApplication(mReactInstanceManager, "MyReactNativeApp", null);
    
            setContentView(mReactRootView);
    
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                if (!Settings.canDrawOverlays(this)) {
                    Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                            Uri.parse("package:" + getPackageName()));
                    startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
                }
            }
        }
    
        @Override
        protected void onPause() {
            super.onPause();
    
            if (mReactInstanceManager != null) {
                mReactInstanceManager.onHostPause(this);
            }
        }
    
        @Override
        protected void onResume() {
            super.onResume();
    
            if (mReactInstanceManager != null) {
                mReactInstanceManager.onHostResume(this, this);
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
    
            if (mReactInstanceManager != null) {
                mReactInstanceManager.onHostDestroy();
            }
        }
    
        @Override
        public void onBackPressed() {
            if (mReactInstanceManager != null) {
                mReactInstanceManager.onBackPressed();
            } else {
                super.onBackPressed();
            }
        }
    
        @Override
        public void invokeDefaultOnBackPressed() {
            super.onBackPressed();
        }
    
        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    if (!Settings.canDrawOverlays(this)) {
                        // SYSTEM_ALERT_WINDOW permission not granted...
                    }
                }
            }
        }
        @Override
        public boolean onKeyUp(int keyCode, KeyEvent event) {
            if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
                mReactInstanceManager.showDevOptionsDialog();
                return true;
            }
            return super.onKeyUp(keyCode, event);
        }
    }
    
     

    C:\code\ReactNativeDemo\app\src\main\assets

    创建assets目录.

     

    4. 尝试编译ReactNativeDemo,发现一些错误修改ReactAndroid源码的gradle的文件

    C:\code\ReactNative\sourcecode2\node_modules\react-native\ReactAndroid\build.gradle

    android {
        compileSdkVersion 26
        buildToolsVersion "26.0.1"
    
        defaultConfig {
            minSdkVersion 16
            targetSdkVersion 26
            versionCode 1
            versionName "1.0"
    dependencies {
        compile fileTree(dir: 'src/main/third-party/java/infer-annotations/', include: ['*.jar'])
        compile 'javax.inject:javax.inject:1'
        compile 'com.android.support:appcompat-v7:26.+'
        compile 'com.facebook.fbui.textlayoutbuilder:textlayoutbuilder:1.0.0'
        compile 'com.facebook.fresco:fresco:1.3.0'
        compile 'com.facebook.fresco:imagepipeline-okhttp3:1.3.0'
        compile 'com.facebook.soloader:soloader:0.1.0'
        compile 'com.google.code.findbugs:jsr305:2.0.1'
        compile 'com.squareup.okhttp3:okhttp:3.6.0'
    
     

    5. 修改后再尝试ReactNativeDemo编译OK

     

    6. 创建自己的RN Helloworld app 

    (1) 创建空目录: C:\code\ReactNative\RNDemoApp

    (2) 创建文件: C:\code\ReactNative\RNDemoApp\package.json

    {

     "name": "MyReactNativeApp",

     "version": "0.0.1",

     "private": true,

     "scripts": {

       "start": "node node_modules/react-native/local-cli/cli.jsstart"

      },

     "dependencies": {

        "react": "16.3.1",

        "react-native": "0.54.4"

      }

    }

    (3) 创建文件: C:\code\ReactNative\RNDemoApp\index.js

    import React from 'react';

    import {

     AppRegistry,

     StyleSheet,

     Text,

     View

    } from 'react-native';

     

    class HelloWorld extends React.Component {

     render() {

       return (

         <View style={styles.container}>

           <Text style={styles.hello}>Hello, World</Text>

         </View>

        )

      }

    }

    var styles = StyleSheet.create({

     container: {

       flex: 1,

       justifyContent: 'center',

      },

     hello: {

       fontSize: 20,

       textAlign: 'center',

       margin: 10,

      },

    });

     

    AppRegistry.registerComponent('MyReactNativeApp',() => HelloWorld);

     

    (4)下载安装依赖包

    $ cd /c/code/ReactNative/RNDemoApp

    $ npm install

     

    (5) 编译打包到assets目录下

     

    $ cd /c/code/ReactNative/RNDemoApp

    $ react-native bundle--platform android --dev true --entry-file index.js --bundle-output../../ReactNativeDemo/app/src/main/assets/index.android.bundle --assets-dest../../ReactNativeDemo/app/src/main/res/

     

    发现生成下边文件:

     

    C:\code\ReactNativeDemo\app\src\main\assets\index.android.bundle

    C:\code\ReactNativeDemo\app\src\main\assets\index.android.bundle.meta

     

    7. 编译ReactNativeDemo工程运行

     

     

    8. 总结:

    (1) 必须用android-ndk-r10e,高版本编译C++过程会报错。

    (2) react native的源码版本一定要和 RN app中的package.json指定的react native版本对上.使用npm install --save react-native#master下载的代码编译一直会出redbox,提示react native版本为0.0.0。不知道什么原因,改为使用npm install --save react-native@0.54.4 package.json指定同样的版本,解决问题。

    (3) 最简单的RN helloworld,编译出的apk大小为6.44MB,查看apk包的源文件有些不必要的so库删除,应该可以精简到3MB左右。

     

    9. 参考:

    https://reactnative.cn/docs/0.51/android-building-from-source.html#content

    https://reactnative.cn/docs/0.51/integration-with-existing-apps.html

    https://reactnative.cn/docs/0.51/integration-with-existing-apps.html#content

    https://reactnative.cn/docs/0.51/getting-started.html

    https://blog.csdn.net/binglan520/article/details/61420859

    https://www.jianshu.com/p/98c8f2a970eb

     

     

     

     

     

    展开全文
  • ReactNative源码

    2017-09-28 14:26:00
    本篇系列文章主要分析ReactNative源码,分析ReactNative的启动流程、渲染原理、通信机制与...1ReactNative源码篇:源码初识2ReactNative源码篇:代码调用3ReactNative源码篇:启动流程4ReactNative源码篇:渲染

    https://juejin.im/post/59cc51625188257a134ab5df

    本篇系列文章主要分析ReactNative源码,分析ReactNative的启动流程、渲染原理、通信机制与线程模型等方面内容。

    通信,指的是RN中Java与JS的通信,即JS中的JSX代码如何转化成Java层真实的View与事件的,以及JavaFile层又是如何调用JS来找出它需要的View与
    事件的。

    在上一篇文章ReactNative源码篇:启动流程中,我们知道RN应用在启动的时候会创建JavaScriptModule注册表(JavaScriptModuleRegistry)与NativeModule注册表(NativeModuleRegistry),RN中Java层
    与JS层的通信就是通过这两张表来完成的,我们来详细看一看。

    在正式开始分析通信机制之前,我们先了解和本篇文章相关的一些重要概念。

    在正式介绍通信机制之前,我们先来了解一些核心的概念。

    JavaScript Module注册表

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

    JavaScriptModule

    JavaScriptModule:这是一个接口,JS Module都会继承此接口,它表示在JS层会有一个相同名字的js文件,该js文件实现了该接口定义的方法,JavaScriptModuleRegistry会利用
    动态代理将这个接口生成代理类,并通过C++传递给JS层,进而调用JS层的方法。

    JavaScriptModuleRegistration

    JavaScriptModuleRegistration用来描述JavaScriptModule的相关信息,它利用反射获取接口里定义的Method。

    JavaScriptModuleRegistry

    JavaScriptModuleRegistry:JS Module注册表,内部维护了一个HashMap:HashMap<Class<? extends JavaScriptModule>, JavaScriptModuleRegistration> mModuleRegistrations,
    JavaScriptModuleRegistry利用动态代理生成接口JavaScriptModule对应的代理类,再通过C++传递到JS层,从而调用JS层的方法。

    Java Module注册表

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

    NativeModule

    NativeModule:是一个接口,实现了该接口则可以被JS层调用,我们在为JS层提供Java API时通常会继承BaseJavaModule/ReactContextBaseJavaModule,这两个类就
    实现了NativeModule接口。

    ModuleHolder

    ModuleHolder:NativeModule的一个Holder类,可以实现NativeModule的懒加载。

    NativeModuleRegistry

    NativeModuleRegistry:Java Module注册表,内部持有Map:Map<Class<? extends NativeModule>, ModuleHolder> mModules,NativeModuleRegistry可以遍历
    并返回Java Module供调用者使用。

    好,了解了这些重要概念,我们开始分析整个RN的通信机制。

    一 配置表的实现

    1.1 配置表的定义

    函数配置表定义在NativeModule.h中。

    struct MethodDescriptor {
      std::string name;
      // type is one of js MessageQueue.MethodTypes
      std::string type;
    
      MethodDescriptor(std::string n, std::string t)
          : name(std::move(n))
          , type(std::move(t)) {}
    };

    method的定义

    public class JavaModuleWrapper {
      public class MethodDescriptor {
        @DoNotStrip
        Method method;
        @DoNotStrip
        String signature;
        @DoNotStrip
        String name;
        @DoNotStrip
        String type;
      }
    }

    1.2 配置表的创建

    在文章ReactNative源码篇:启动流程中,我们可以知道在ReactInstanceManager执行createReactContext()时
    创建了JavaScriptModuleRegistry与NativeModuleRegistry这两张表,我们跟踪一下它们俩的创建流程,以及创建完成后各自的去向。

    通过上面分析我们可知JavaScriptModuleRegistry创建完成后由CatalystInstanceImpl实例所持有,供后续Java调用JS时使用。相当于Java层此时持有了一张JS模块表,它以后可以通过这个表去调用JS。
    而NativeModuleRegistry在CatalystInstanceImpl.initializeBridge()方法中被传入C++层,最终可以被JS层所拿到。

    public class CatalystInstanceImpl(
      private CatalystInstanceImpl(
          final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,
          final JavaScriptExecutor jsExecutor,
          final NativeModuleRegistry registry,
          final JavaScriptModuleRegistry jsModuleRegistry,
          final JSBundleLoader jsBundleLoader,
          NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
    
        ...
    
        initializeBridge(
          new BridgeCallback(this),
          jsExecutor,
          mReactQueueConfiguration.getJSQueueThread(),
          mReactQueueConfiguration.getNativeModulesQueueThread(),
          mJavaRegistry.getJavaModules(this),//传入的是Collection<JavaModuleWrapper> ,JavaModuleWrapper是NativeHolder的一个Wrapper类,它对应了C++层JavaModuleWrapper.cpp.
          mJavaRegistry.getCxxModules());//传入的是Collection<ModuleHolder> ,ModuleHolder是NativeModule的一个Holder类,可以实现NativeModule的懒加载。
        FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge");
        mMainExecutorToken = getMainExecutorToken();
      }
    )

    这里我们注意一下传入C++的两个集合

    mJavaRegistry.getJavaModules(this):传入的是Collection<JavaModuleWrapper> ,JavaModuleWrapper是NativeHolder的一个Wrapper类,它对应了C++层JavaModuleWrapper.cpp,
    JS在Java的时候最终会调用到这个类的inovke()方法上。
    
    mJavaRegistry.getCxxModules()):传入的是Collection<ModuleHolder> ,ModuleHolder是NativeModule的一个Holder类,可以实现NativeModule的懒加载。

    我们继续跟踪这两个集合到了C++层之后的去向。

    CatalystInstanceImpl.cpp

    void CatalystInstanceImpl::initializeBridge(
        jni::alias_ref<ReactCallback::javaobject> callback,
        // This executor is actually a factory holder.
        JavaScriptExecutorHolder* jseh,
        jni::alias_ref<JavaMessageQueueThread::javaobject> jsQueue,
        jni::alias_ref<JavaMessageQueueThread::javaobject> moduleQueue,
        jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
        jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject> cxxModules) {
    
    
      instance_->initializeBridge(folly::make_unique<JInstanceCallback>(callback),
                                  jseh->getExecutorFactory(),
                                  folly::make_unique<JMessageQueueThread>(jsQueue),
                                  folly::make_unique<JMessageQueueThread>(moduleQueue),
                                  buildModuleRegistry(std::weak_ptr<Instance>(instance_),
                                                      javaModules, cxxModules));
    }

    这两个集合在CatalystInstanceImpl::initializeBridge()被打包成ModuleRegistry传入Instance.cpp.、,如下所示:

    ModuleRegistryBuilder.cpp

    std::unique_ptr<ModuleRegistry> buildModuleRegistry(
        std::weak_ptr<Instance> winstance,
        jni::alias_ref<jni::JCollection<JavaModuleWrapper::javaobject>::javaobject> javaModules,
        jni::alias_ref<jni::JCollection<ModuleHolder::javaobject>::javaobject> cxxModules) {
    
      std::vector<std::unique_ptr<NativeModule>> modules;
      for (const auto& jm : *javaModules) {
        modules.emplace_back(folly::make_unique<JavaNativeModule>(winstance, jm));
      }
      for (const auto& cm : *cxxModules) {
        modules.emplace_back(
          folly::make_unique<CxxNativeModule>(winstance, cm->getName(), cm->getProvider()));
      }
      if (modules.empty()) {
        return nullptr;
      } else {
        return folly::make_unique<ModuleRegistry>(std::move(modules));
      }
    }

    打包好的ModuleRegistry通过Instance::initializeBridge()传入到NativeToJsBridge.cpp中,并在NativeToJsBridge的构造方法中传给JsToNativeBridge,以后JS如果调用Java就可以通过
    ModuleRegistry来进行调用。

    二 通信桥的实现

    关于整个RN的通信机制,可以用一句话来概括:

    JNI作为C++与Java的桥梁,JSC作为C++与JavaScript的桥梁,而C++最终连接了Java与JavaScript。

    RN应用通信桥结构图如下所示:

    2.1 关于通信桥在Java层中的实现

    从文章ReactNative源码篇:启动流程我们得知在

    在CatalystInstanceImpl构造方法中做了初始化通信桥的操作:

    public class CatalystInstanceImpl implements CatalystInstance {
    
    private CatalystInstanceImpl(
          final ReactQueueConfigurationSpec ReactQueueConfigurationSpec,
          final JavaScriptExecutor jsExecutor,
          final NativeModuleRegistry registry,
          final JavaScriptModuleRegistry jsModuleRegistry,
          final JSBundleLoader jsBundleLoader,
          NativeModuleCallExceptionHandler nativeModuleCallExceptionHandler) {
    
        ...
    
        //Native方法,调用initializeBridge()方法,并创建BridgeCallback实例,初始化Bridge。
        initializeBridge(
          new BridgeCallback(this),
          jsExecutor,
          mReactQueueConfiguration.getJSQueueThread(),
          mReactQueueConfiguration.getNativeModulesQueueThread(),
          mJavaRegistry.getJavaModules(this),
          mJavaRegistry.getCxxModules());
        FLog.w(ReactConstants.TAG, "Initializing React Xplat Bridge after initializeBridge");
        mMainExecutorToken = getMainExecutorToken();
      }
    
      //在C++层初始化通信桥ReactBridge
      private native void initializeBridge(
          ReactCallback callback,
          JavaScriptExecutor jsExecutor,
          MessageQueueThread jsQueue,
          MessageQueueThread moduleQueue,
          Collection<JavaModuleWrapper> javaModules,
          Collection<ModuleHolder> 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<JavaModuleWrapper> javaModules:java modules,来源于mJavaRegistry.getJavaModules(this)。
    Collection<ModuleHolder> cxxModules):c++ modules,来源于mJavaRegistry.getCxxModules()。

    我们去跟踪它的函数调用链:

    CatalystInstanceImpl.initializeBridge() -> CatalystInstanceImpl::initializeBridge() -> Instance::initializeBridge()

    最终可以发现,Instance::initializeBridge()实现了ReactBridge(C++层对应JsToNativeBridge与NativeToJsBridge)的初始化。

    Instance.cpp

    void Instance::initializeBridge(
        std::unique_ptr<InstanceCallback> callback,
        std::shared_ptr<JSExecutorFactory> jsef,
        std::shared_ptr<MessageQueueThread> jsQueue,
        std::unique_ptr<MessageQueueThread> nativeQueue,
        std::shared_ptr<ModuleRegistry> moduleRegistry) {
      callback_ = std::move(callback);
    
      if (!nativeQueue) {
        // TODO pass down a thread/queue from java, instead of creating our own.
    
        auto queue = folly::make_unique<CxxMessageQueue>();
        std::thread t(queue->getUnregisteredRunLoop());
        t.detach();
        nativeQueue = std::move(queue);
      }
    
      jsQueue->runOnQueueSync(
        [this, &jsef, moduleRegistry, jsQueue,
         nativeQueue=folly::makeMoveWrapper(std::move(nativeQueue))] () mutable {
          //初始化NativeToJsBridge对象
          nativeToJsBridge_ = folly::make_unique<NativeToJsBridge>(
              jsef.get(), moduleRegistry, jsQueue, nativeQueue.move(), callback_);
    
          std::lock_guard<std::mutex> lock(m_syncMutex);
          m_syncReady = true;
          m_syncCV.notify_all();
        });
    
      CHECK(nativeToJsBridge_);
    }

    我们接着去C++层看看JsToNativeBridge与NativeToJsBridge的实现。

    2.2 关于通信桥在C++层的实现

    在C++层的Executor.h文件中定义了整个Java调用JS,JS调用Java的逻辑。

    Executor.h文件中定义了抽象类ExecutorDelegate,定义了执行Native Module的方法,它是JS调用Java的桥梁,JsToNativeBridge实现了该类的纯虚函数(抽象方法),该抽象
    类还持有JSExecutor(用来执行JS)的引用。

    class ExecutorDelegate {
     public:
      virtual ~ExecutorDelegate() {}
    
      virtual void registerExecutor(std::unique_ptr<JSExecutor> executor,
                                    std::shared_ptr<MessageQueueThread> queue) = 0;
      virtual std::unique_ptr<JSExecutor> unregisterExecutor(JSExecutor& executor) = 0;
    
      virtual std::shared_ptr<ModuleRegistry> getModuleRegistry() = 0;
    
      virtual void callNativeModules(
        JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) = 0;
      virtual MethodCallResult callSerializableNativeHook(
        JSExecutor& executor, unsigned int moduleId, unsigned int methodId, folly::dynamic&& args) = 0;
    };

    Executor.h文件中定义了抽象类JSExecutor,它定义了执行JS Module的方法,用来解释执行JS,JSCExecutor实现了该类中的纯虚函数(抽象方法),另一个类
    NativeToBridge与JsToNativeBridge相对应,它是Java调用JS的桥梁,NativeToBridge持有JSCExecutor的引用,如果NativeToBridge需要执行JS时就会
    去调用JSCExecutor。

    
    class JSExecutor {
    public:
      /**
       * Execute an application script bundle in the JS context.
       */
      virtual void loadApplicationScript(std::unique_ptr<const JSBigString> script,
                                         std::string sourceURL) = 0;
    
      /**
       * Add an application "unbundle" file
       */
      virtual void setJSModulesUnbundle(std::unique_ptr<JSModulesUnbundle> bundle) = 0;
    
      /**
       * Executes BatchedBridge.callFunctionReturnFlushedQueue with the module ID,
       * method ID and optional additional arguments in JS. The executor is responsible
       * for using Bridge->callNativeModules to invoke any necessary native modules methods.
       */
      virtual void callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) = 0;
    
      /**
       * Executes BatchedBridge.invokeCallbackAndReturnFlushedQueue with the cbID,
       * and optional additional arguments in JS and returns the next queue. The executor
       * is responsible for using Bridge->callNativeModules to invoke any necessary
       * native modules methods.
       */
      virtual void invokeCallback(const double callbackId, const folly::dynamic& arguments) = 0;
    
      virtual void setGlobalVariable(std::string propName,
                                     std::unique_ptr<const JSBigString> jsonValue) = 0;
      virtual void* getJavaScriptContext() {
        return nullptr;
      }
      virtual bool supportsProfiling() {
        return false;
      }
      virtual void startProfiler(const std::string &titleString) {}
      virtual void stopProfiler(const std::string &titleString, const std::string &filename) {}
      virtual void handleMemoryPressureUiHidden() {}
      virtual void handleMemoryPressureModerate() {}
      virtual void handleMemoryPressureCritical() {
        handleMemoryPressureModerate();
      }
      virtual void destroy() {}
      virtual ~JSExecutor() {}
    };

    2.3 关于通信桥在JS层的实现

    JS层Libraries/BatchedBridge包下面有3个JS文件:BatchedBridge.js、MessageQueue.js、NativeModules.js,它们封装了通信桥在JS层的实现。

    BatchedBridge.js

    'use strict';
    
    const MessageQueue = require('MessageQueue');
    const BatchedBridge = new MessageQueue();
    
    // Wire up the batched bridge on the global object so that we can call into it.
    // Ideally, this would be the inverse relationship. I.e. the native environment
    // provides this global directly with its script embedded. Then this module
    // would export it. A possible fix would be to trim the dependencies in
    // MessageQueue to its minimal features and embed that in the native runtime.
    
    Object.defineProperty(global, '__fbBatchedBridge', {
      configurable: true,
      value: BatchedBridge,
    });
    
    module.exports = BatchedBridge;

    可以看到在BatchedBridge中创建了MessageQueue对象。

    MessageQueue.js

    MessageQueue的实现有点长,我们来一步步看它的实现。先看MessageQueue的构造方法。

    class MessageQueue {
    
      //
      _callableModules: {[key: string]: Object};
      //队列,分别存放要调用的模块数组、方法数组、参数数组与数组大小
      _queue: [Array<number>, Array<number>, Array<any>, number];
      //回调函数数组,与_quueue一一对应,每个_queue中调用的方法,如果有回调方法,那么就在这个数组对应的下标上。
      _callbacks: [];
      //回调ID,自动增加。
      _callbackID: number;
      //调用函数ID,自动增加。
      _callID: number;
      _lastFlush: number;
      //消息队列事件循环开始时间
      _eventLoopStartTime: number;
    
      _debugInfo: Object;
      //Module Table,用于Native Module
      _remoteModuleTable: Object;
      //Method Table,用于Native Module
      _remoteMethodTable: Object;
    
      __spy: ?(data: SpyData) => void;
    
      constructor() {
        this._callableModules = {};
        this._queue = [[], [], [], 0];
        this._callbacks = [];
        this._callbackID = 0;
        this._callID = 0;
        this._lastFlush = 0;
        this._eventLoopStartTime = new Date().getTime();
    
        if (__DEV__) {
          this._debugInfo = {};
          this._remoteModuleTable = {};
          this._remoteMethodTable = {};
        }
    
        //绑定函数,也就是创建一个函数,使这个函数不论怎么调用都有同样的this值。
        (this:any).callFunctionReturnFlushedQueue = this.callFunctionReturnFlushedQueue.bind(this);
        (this:any).callFunctionReturnResultAndFlushedQueue = this.callFunctionReturnResultAndFlushedQueue.bind(this);
        (this:any).flushedQueue = this.flushedQueue.bind(this);
        (this:any).invokeCallbackAndReturnFlushedQueue = this.invokeCallbackAndReturnFlushedQueue.bind(this);
      }
    }

    NativeModules.jS

    NativeModules描述了Module的结构,用于解析并生成Module。

    Module的结构定义如下所示:

    type ModuleConfig = [
      string, /* name */
      ?Object, /* constants */
      Array<string>, /* functions */
      Array<number>, /* promise method IDs */
      Array<number>, /* sync method IDs */
    ];

    举例

    {
      "remoteModuleConfig": {
        "Logger": {
          "constants": { /* If we had exported constants... */ },
          "moduleID": 1,
          "methods": {
            "requestPermissions": {
              "type": "remote",
              "methodID": 1
            }
          }
        }
      }
    }
    {
      'ToastAndroid': {
        moduleId: 0,
        methods: {
          'show': {
            methodID: 0
          }
        },
        constants: {
          'SHORT': '0',
          'LONG': '1'
        }
      },
      'moduleB': {
        moduleId: 0,
        methods: {
          'method1': {
            methodID: 0
          }
        },
        'key1': 'value1',
        'key2': 'value2'
      }
    }

    接下来,我们分析一下JS代码调用Java代码的流程。

    三 JS层调用Java层

    RN应用通信机制流程图(JS->Java)如下所示:

    举例

    同样的,我们先来看一个JS代码调用Java代码的例子,我们写一个ToastModule供JS代码调用。

    1 编写ToastModule继承于ReactContextBaseJavaModule,该ToastModule实现具体的功能供JS代码调用。

    public class ToastModule extends ReactContextBaseJavaModule {
    
      private static final String DURATION_SHORT_KEY = "SHORT";
      private static final String DURATION_LONG_KEY = "LONG";
    
      public ToastModule(ReactApplicationContext reactContext) {
        super(reactContext);
      }
    
      //返回模块名字供JS代码调用
      @Override
      public String getName() {
        return "ToastAndroid";
      }
    
      //返回常量供JS代码调用
      @Override
      public Map<String, Object> getConstants() {
        final Map<String, Object> constants = new HashMap<>();
        constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT);
        constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG);
        return constants;
      }
    
      //暴露给JS代码的方法,加@ReactMethod注解,且该方法总是void。
      @ReactMethod
      public void show(String message, int duration) {
        Toast.makeText(getReactApplicationContext(), message, duration).show();
      }
    }

    2 编写类继承ReactPackage,注册ToastModule。

    public class AnExampleReactPackage implements ReactPackage {
    
      @Override
      public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
      }
    
      @Override
      public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
      }
    
      @Override
      public List<NativeModule> createNativeModules(
                                  ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
    
        modules.add(new ToastModule(reactContext));
    
        return modules;
      }
    
    }
    protected List<ReactPackage> getPackages() {
        return Arrays.<ReactPackage>asList(
                new MainReactPackage(),
                new AnExampleReactPackage()); // <-- Add this line with your package name.
    }

    3 为了方便JS代码调用,编写一个JS Module来包装Native Module的功能。

    
    'use strict';
    /**
     * This exposes the native ToastAndroid module as a JS module. This has a
     * function 'show' which takes the following parameters:
     *
     * 1. String message: A string with the text to toast
     * 2. int duration: The duration of the toast. May be ToastAndroid.SHORT or
     *    ToastAndroid.LONG
     */
    import { NativeModules } from 'react-native';
    module.exports = NativeModules.ToastAndroid;

    4 最后我们就可以直接在JS代码中进行调用。

    
    import ToastAndroid from './ToastAndroid';
    
    ToastAndroid.show('Awesome', ToastAndroid.SHORT);

    以上便是JS代码调用Java代码的全部流程,我们来具体分析它的实现细节。

    从上面例子中,我们可以看出,调用的第一步就是从JS层的NativeModule注册表中拿到对应Java层的Java Module。但是JS在调用Java并不是通过接口来进行的,而是对应的
    参数moduleID、methodID都push到一个messageQueue中,等待Java层的事件来驱动它,当Java层的事件传递过来以后,JS层把messageQUeue中的所有数据返回到Java层,
    再通过注册表JavaRegistry去调用方法。

    JS层调用Java层实现流程:

    1 JS将需要调用的方法的moduleID、methodID与arguments一块push到MessageQueue中,等待Java层的事件驱动。
    2

    我们们再调用Java代码时都通过NativeModules.xxxModule.xxxMethod()的方式来调用,我们先来看看NativeModules.js的实现。

    3.1 NativeModules.xxxModule.xxxMethod()

    当我们用NativeModules.xxxModule.xxxMethod()这种方式去调用时,JS就会通过JS层的NativeModules去查找相对应的Java Module。

    NativeModules.js

    
    let NativeModules : {[moduleName: string]: Object} = {};
    
      //可以看到Native被赋值为global.nativeModuleProxy,nativeModuleProxy是一个全局变量,顾名思义,它是NativeModules.js在C++层的代理。
      NativeModules = global.nativeModuleProxy;
    } else {
      const bridgeConfig = global.__fbBatchedBridgeConfig;
      invariant(bridgeConfig, '__fbBatchedBridgeConfig is not set, cannot invoke native modules');
    
      (bridgeConfig.remoteModuleConfig || []).forEach((config: ModuleConfig, moduleID: number) => {
        // Initially this config will only contain the module name when running in JSC. The actual
        // configuration of the module will be lazily loaded.
        const info = genModule(config, moduleID);
        if (!info) {
          return;
        }
    
        if (info.module) {
          NativeModules[info.name] = info.module;
        }
        // If there's no module config, define a lazy getter
        else {
          defineLazyObjectProperty(NativeModules, info.name, {
            get: () => loadModule(info.name, moduleID)
          });
        }
      });
    }
    
    module.exports = NativeModules;

    nativeModuleProxy实质上是在启动流程中,JSCExecutor::JSCExecutor()在创建时通过installGlobalProxy(m_context, "nativeModuleProxy", exceptionWrapMethod<&JSCExecutor::getNativeModule>())
    创建的,所以当JS调用NativeModules时,实际上在调用JSCExecutor::getNativeModule()方法,我们来看一看该方法的实现。

    3.2 JSCExecutor::getNativeModule(JSObjectRef object, JSStringRef propertyName)

    
    JSValueRef JSCExecutor::getNativeModule(JSObjectRef object, JSStringRef propertyName) {
      if (JSC_JSStringIsEqualToUTF8CString(m_context, propertyName, "name")) {
        return Value(m_context, String(m_context, "NativeModules"));
      }
      //m_nativeModules的类型是JSCNativeModules
      return m_nativeModules.getModule(m_context, propertyName);
    }

    该方法进一步调用JSCNativeModules.cpp的getModule()方法,我们来看看它的实现。

    3.3 JSCNativeModules::getModule(JSContextRef context, JSStringRef jsName)

    JSCNativeModules.cpp

    JSValueRef JSCNativeModules::getModule(JSContextRef context, JSStringRef jsName) {
      if (!m_moduleRegistry) {
        return Value::makeUndefined(context);
      }
    
      std::string moduleName = String::ref(context, jsName).str();
    
      const auto it = m_objects.find(moduleName);
      if (it != m_objects.end()) {
        return static_cast<JSObjectRef>(it->second);
      }
    
      //调用该方法,通过JSC获取全局设置的JS属性,然后通过JNI查找Java层注册表,再触发JS层方法。
      auto module = createModule(moduleName, context);
      if (!module.hasValue()) {
        return Value::makeUndefined(context);
      }
    
      // Protect since we'll be holding on to this value, even though JS may not
      module->makeProtected();
    
      auto result = m_objects.emplace(std::move(moduleName), std::move(*module)).first;
      return static_cast<JSObjectRef>(result->second);
    }
    
    folly::Optional<Object> JSCNativeModules::createModule(const std::string& name, JSContextRef context) {
      if (!m_genNativeModuleJS) {
    
        auto global = Object::getGlobalObject(context);
        //NativeModules.js中将全局变量__fbGenNativeModule指向了函数NativeModules::gen()方法,此处
        //便是去获取JSC去调用这个方法
        m_genNativeModuleJS = global.getProperty("__fbGenNativeModule").asObject();
        m_genNativeModuleJS->makeProtected();
    
        // Initialize the module name list, otherwise getModuleConfig won't work
        // TODO (pieterdb): fix this in ModuleRegistry
        m_moduleRegistry->moduleNames();
      }
      //获取Native配置表
      auto result = m_moduleRegistry->getConfig(name);
      if (!result.hasValue()) {
        return nullptr;
      }
    
      //调用NativeModules::gen()方法
      Value moduleInfo = m_genNativeModuleJS->callAsFunction({
        Value::fromDynamic(context, result->config),
        Value::makeNumber(context, result->index)
      });
      CHECK(!moduleInfo.isNull()) << "Module returned from genNativeModule is null";
    
      return moduleInfo.asObject().getProperty("module").asObject();
    }

    上面的方法实现的功能分为2步:

    1 调用ModuleRegistry::getConfig()获取ModuleConfig。
    2 通过JSC调用NativeModules.js的genModule()方法

    ModuleRegistry是从Java层传递过来额JavaModuleRegistry,ModuleRegistry::getConfig()查询Java Module的配置表,然后发送给NativeModule.js生成对应的JS Module。

    ModuleRegistry.cpp

    
    folly::Optional<ModuleConfig> ModuleRegistry::getConfig(const std::string& name) {
      SystraceSection s("getConfig", "module", name);
      auto it = modulesByName_.find(name);
      if (it == modulesByName_.end()) {
        return nullptr;
      }
    
      CHECK(it->second < modules_.size());
      //modules_列表来源于CatalystInstanceImpl::initializeBridge()
      //module实质上是ModuleRegistryHolder.cpp的构造函数汇总将Java层传递过来的Module包装成CxxNativeModule与JavaModule,这两个都是NativeModule的子类。
      NativeModule* module = modules_[it->second].get();
    
      //string name, object constants, array methodNames准备创建一个动态对象。
      // string name, object constants, array methodNames (methodId is index), [array promiseMethodIds], [array syncMethodIds]
      folly::dynamic config = folly::dynamic::array(name);
    
      {
        SystraceSection s("getConstants");
        //通过反射调用Java层的JavaModuleWrapper.getContants()shi方法。
        config.push_back(module->getConstants());
      }
    
      {
        SystraceSection s("getMethods");
        //通过反射调用Java层的JavaModuleWrapper.getMethods()方法,也就是BaseJavaModule.getMethods(),该方法内部会调用
        //findMethos()方法查询带有ReactMoethod注解的方法。
        std::vector<MethodDescriptor> methods = module->getMethods();
    
        folly::dynamic methodNames = folly::dynamic::array;
        folly::dynamic promiseMethodIds = folly::dynamic::array;
        folly::dynamic syncMethodIds = folly::dynamic::array;
    
        for (auto& descriptor : methods) {
          // TODO: #10487027 compare tags instead of doing string comparison?
          methodNames.push_back(std::move(descriptor.name));
          if (descriptor.type == "promise") {
            promiseMethodIds.push_back(methodNames.size() - 1);
          } else if (descriptor.type == "sync") {
            syncMethodIds.push_back(methodNames.size() - 1);
          }
        }
    
        if (!methodNames.empty()) {
          config.push_back(std::move(methodNames));
          if (!promiseMethodIds.empty() || !syncMethodIds.empty()) {
            config.push_back(std::move(promiseMethodIds));
            if (!syncMethodIds.empty()) {
              config.push_back(std::move(syncMethodIds));
            }
          }
        }
      }
    
      if (config.size() == 1) {
        // no constants or methods
        return nullptr;
      } else {
        return ModuleConfig({it->second, config});
      }
    }

    获取到相应ModuleConfig就会去调用NativeModules.js的genModule()生成JS将要调用对应的JS Module。

    3.4 NativeModules.genModule(config: ?ModuleConfig, moduleID: number): ?{name: string, module?: Object}

    NativeModules.js

    
    // export this method as a global so we can call it from native
    global.__fbGenNativeModule = genModule;
    
    function genModule(config: ?ModuleConfig, moduleID: number): ?{name: string, module?: Object} {
      if (!config) {
        return null;
      }
    
      //通过JSC拿到C++中从Java层获取的Java Module的注册表
      const [moduleName, constants, methods, promiseMethods, syncMethods] = config;
      invariant(!moduleName.startsWith('RCT') && !moduleName.startsWith('RK'),
        'Module name prefixes should\'ve been stripped by the native side ' +
        'but wasn\'t for ' + moduleName);
    
      if (!constants && !methods) {
        // Module contents will be filled in lazily later
        return { name: moduleName };
      }
    
      const module = {};
      //遍历构建Module的属性与方法
      methods && methods.forEach((methodName, methodID) => {
        const isPromise = promiseMethods && arrayContains(promiseMethods, methodID);
        const isSync = syncMethods && arrayContains(syncMethods, methodID);
        invariant(!isPromise || !isSync, 'Cannot have a method that is both async and a sync hook');
        const methodType = isPromise ? 'promise' : isSync ? 'sync' : 'async';
        //生成Module的函数方法
        module[methodName] = genMethod(moduleID, methodID, methodType);
      });
      Object.assign(module, constants);
    
      if (__DEV__) {
        BatchedBridge.createDebugLookup(moduleID, moduleName, methods);
      }
    
      return { name: moduleName, module };
    }

    该方法通过JSC拿到C++中从Java层获取的Java Module的注册表,遍历构建Module的属性与方法,并调用genMethod()生成Module的函数方法,我们调用ToastAndroid.show(‘Awesome’, ToastAndroid.SHORT)时
    实际上就是在调用genMethod()生成的方法,我们来看一看它的实现。

    NativeModules.js

    //该函数会根据函数类型的不同做不同的处理,但最终都会调用BatchedBridge.enqueueNativeCall()方法。
    function genMethod(moduleID: number, methodID: number, type: MethodType) {
      let fn = null;
      if (type === 'promise') {
        fn = function(...args: Array<any>) {
          return new Promise((resolve, reject) => {
            BatchedBridge.enqueueNativeCall(moduleID, methodID, args,
              (data) => resolve(data),
              (errorData) => reject(createErrorFromErrorData(errorData)));
          });
        };
      } else if (type === 'sync') {
        fn = function(...args: Array<any>) {
          return global.nativeCallSyncHook(moduleID, methodID, args);
        };
      } else {
        fn = function(...args: Array<any>) {
          const lastArg = args.length > 0 ? args[args.length - 1] : null;
          const secondLastArg = args.length > 1 ? args[args.length - 2] : null;
          const hasSuccessCallback = typeof lastArg === 'function';
          const hasErrorCallback = typeof secondLastArg === 'function';
          hasErrorCallback && invariant(
            hasSuccessCallback,
            'Cannot have a non-function arg after a function arg.'
          );
          const onSuccess = hasSuccessCallback ? lastArg : null;
          const onFail = hasErrorCallback ? secondLastArg : null;
          const callbackCount = hasSuccessCallback + hasErrorCallback;
          args = args.slice(0, args.length - callbackCount);
          BatchedBridge.enqueueNativeCall(moduleID, methodID, args, onFail, onSuccess);
        };
      }
      fn.type = type;
      return fn;
    }

    该函数会根据函数类型的不同做不同的处理,但最终都会调用BatchedBridge.enqueueNativeCall()方法,我们来看看它的实现。

    3.5 MessageQueue.enqueueNativeCall(moduleID: number, methodID: number, params: Array, onFail: ?Function, onSucc: ?Function)

    MessageQueue.js

    
      enqueueNativeCall(moduleID: number, methodID: number, params: Array<any>, onFail: ?Function, onSucc: ?Function) {
        if (onFail || onSucc) {
          if (__DEV__) {
            const callId = this._callbackID >> 1;
            this._debugInfo[callId] = [moduleID, methodID];
            if (callId > DEBUG_INFO_LIMIT) {
              delete this._debugInfo[callId - DEBUG_INFO_LIMIT];
            }
          }
          onFail && params.push(this._callbackID);
          /* $FlowFixMe(>=0.38.0 site=react_native_fb,react_native_oss) - Flow error
           * detected during the deployment of v0.38.0. To see the error, remove
           * this comment and run flow */
          this._callbacks[this._callbackID++] = onFail;
          onSucc && params.push(this._callbackID);
          /* $FlowFixMe(>=0.38.0 site=react_native_fb,react_native_oss) - Flow error
           * detected during the deployment of v0.38.0. To see the error, remove
           * this comment and run flow */
          this._callbacks[this._callbackID++] = onSucc;
        }
    
        if (__DEV__) {
          global.nativeTraceBeginAsyncFlow &&
            global.nativeTraceBeginAsyncFlow(TRACE_TAG_REACT_APPS, 'native', this._callID);
        }
        this._callID++;
    
        //_queue是个队列,用来存放调用的模块、方法与参数信息。
        this._queue[MODULE_IDS].push(moduleID);
        this._queue[METHOD_IDS].push(methodID);
    
        if (__DEV__) {
          // Any params sent over the bridge should be encodable as JSON
          JSON.stringify(params);
    
          // The params object should not be mutated after being queued
          deepFreezeAndThrowOnMutationInDev((params:any));
        }
        this._queue[PARAMS].push(params);
    
        const now = new Date().getTime();
        //如果5ms内有多个方法调用则先待在队列里,防止过高频率的方法调用,否则则调用JSCExecutor::nativeFlushQueueImmediate(size_t argumentCount, const JSValueRef arguments[]) 方法。
        if (global.nativeFlushQueueImmediate &&
            now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) {
          global.nativeFlushQueueImmediate(this._queue);
          this._queue = [[], [], [], this._callID];
          this._lastFlush = now;
        }
        Systrace.counterEvent('pending_js_to_native_queue', this._queue[0].length);
        if (__DEV__ && this.__spy && isFinite(moduleID)) {
          this.__spy(
            { type: TO_NATIVE,
              module: this._remoteModuleTable[moduleID],
              method: this._remoteMethodTable[moduleID][methodID],
              args: params }
          );
        } else if (this.__spy) {
          this.__spy({type: TO_NATIVE, module: moduleID + '', method: methodID, args: params});
        }
      }

    流程走到这个方法的时候,JS层的调用流程就结束了,JS层主要通过JSC桥接获取Java Module的注册表,并把它转换为对应的JS Native Module(属性、方法转换),当JS
    通过NativeModule.xxxMethod()准备调用Java方法时,会把xxxMethod()放进一个JS队列,在队列中:

    1 如果如果5m(MIN_TIME_BETWEEN_FLUSHES_MS)以内,则继续在队列中等待Java层的事件来驱动它。
    2 如果5m(MIN_TIME_BETWEEN_FLUSHES_MS)以内,则直接触发的 JSCExecutor::nativeFlushQueueImmediate(size_t argumentCount, const JSValueRef arguments[]) 方法去调用对应的Java方法。

    事实上,在队列中,如果是Java方法调用JS方法,则会把之前队列里存的方法通过JSCExecutor::flush()进行处理。

    我们再来看看JSCExecutor::nativeFlushQueueImmediate(size_t argumentCount, const JSValueRef arguments[]) 的实现。

    3.6 JSValueRef JSCExecutor::nativeFlushQueueImmediate(size_t argumentCount, const JSValueRef arguments[])

    JSCExecutor.cpp

    JSValueRef JSCExecutor::nativeFlushQueueImmediate(
        size_t argumentCount,
        const JSValueRef arguments[]) {
      if (argumentCount != 1) {
        throw std::invalid_argument("Got wrong number of args");
      }
    
      flushQueueImmediate(Value(m_context, arguments[0]));
      return Value::makeUndefined(m_context);
    }
    
    void JSCExecutor::flushQueueImmediate(Value&& queue) {
      auto queueStr = queue.toJSONString();
      //调用JsToNativeBridge.cpp的callNativeModules(),传入的isEndOfBatch=false
      m_delegate->callNativeModules(*this, folly::parseJson(queueStr), false);
    }

    可以看出nativeFlushQueueImmediate()会进一步调用flushQueueImmediate()方法,m_delegate的类型是ExecutorDelegate,事实上它调用的是ExecutorDelegate的子类
    JsToNativeBridge.cpp的callNativeModules()方法,我们回想一下上面我们分析Java代码调用JS代码第7步的实现,它也同样走到了这个方法,只是传入的isEndOfBatch=true。

    3.7 JsToNativeBridge::callNativeModules()

    JsToNativeBridge.cpp

      void callNativeModules()(
          JSExecutor& executor, folly::dynamic&& calls, bool isEndOfBatch) override {
    
        CHECK(m_registry || calls.empty()) <<
          "native module calls cannot be completed with no native modules";
        ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor);
        //在Native队列中执行
        m_nativeQueue->runOnQueue([this, token, calls=std::move(calls), isEndOfBatch] () mutable {
    
          //遍历来自JS队列的调用方法列表
    
          // An exception anywhere in here stops processing of the batch.  This
          // was the behavior of the Android bridge, and since exception handling
          // terminates the whole bridge, there's not much point in continuing.
          for (auto& call : react::parseMethodCalls(std::move(calls))) {
            //m_registry的类型是ModuleRegistry,
            m_registry->callNativeMethod(
              token, call.moduleId, call.methodId, std::move(call.arguments), call.callId);
          }
          if (isEndOfBatch) {
            //标记回调Java状态
            m_callback->onBatchComplete();
            m_callback->decrementPendingJSCalls();
          }
        });
      }

    在该方法中取出JS队列中的JS调用Java的所有方法,并通过ModuleRegistry::callNativeMethod()方法去遍历调用,我们来看看这个方法的实现。

    3.8 ModuleRegistry::callNativeMethod(ExecutorToken token, unsigned int moduleId, unsigned int methodId, folly::dynamic&& params, int callId)

    void ModuleRegistry::callNativeMethod(ExecutorToken token, unsigned int moduleId, unsigned int methodId,
                                          folly::dynamic&& params, int callId) {
      if (moduleId >= modules_.size()) {
        throw std::runtime_error(
          folly::to<std::string>("moduleId ", moduleId,
                                 " out of range [0..", modules_.size(), ")"));
      }
    
    #ifdef WITH_FBSYSTRACE
      if (callId != -1) {
        fbsystrace_end_async_flow(TRACE_TAG_REACT_APPS, "native", callId);
      }
    #endif
    
      //modules_是创建ModuleRegistryHolder时根据Java层ModuleRegistryHolder创建的C++ NativeModule
      //moduleId为模块在当前列表的索引值
      modules_[moduleId]->invoke(token, methodId, std::move(params));
    }

    modules_的类型是std::vector> modules_,NativeModule是C++层针对Java Module的一种包装,NativeModule的子类是JavaNativeModule,
    我们去看看它的调用方法invoke().

    3.9 JavaNativeModule::invoke(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params)

    抽象类NativeModule定义的纯虚函数(抽象方法)

    NativeModule.cpp

    class NativeModule {
     public:
      virtual ~NativeModule() {}
      virtual std::string getName() = 0;
      virtual std::vector<MethodDescriptor> getMethods() = 0;
      virtual folly::dynamic getConstants() = 0;
      virtual bool supportsWebWorkers() = 0;
      // TODO mhorowitz: do we need initialize()/onCatalystInstanceDestroy() in C++
      // or only Java?
      virtual void invoke(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) = 0;
      virtual MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& args) = 0;
    };
    
    }
    }

    NativeModule有2个子类,它的类图如下所示:

    class JavaNativeModule : public NativeModule {
    
      void JavaNativeModule::invoke(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) {
    
      //wrapper_-的类型是JavaModuleWrapper.cpp,映射Java层的JavaModuleWrapper.java,在ModuleRegistryHolder.cpp构造方法中由Java传入的Java Module被C++包装成
      //JavaModuleWrapper对象。通过反射调用Java层的JavaModuleWrapper.java的invoke()方法,同时把mothodId和参数传过去。
      static auto invokeMethod =
        wrapper_->getClass()->getMethod<void(JExecutorToken::javaobject, jint, ReadableNativeArray::javaobject)>("invoke");
      invokeMethod(wrapper_, JExecutorToken::extractJavaPartFromToken(token).get(), static_cast<jint>(reactMethodId),
                   ReadableNativeArray::newObjectCxxArgs(std::move(params)).get());
    }
    }

    该方法调用通过反射调用Java层的JavaModuleWrapper.java的invoke()方法,同时把mothodId和参数传过去。我们来看看JavaModuleWrapper.java的invoke()方法的实现。

    3.10 JavaModuleWrapper.invoke(ExecutorToken token, int methodId, ReadableNativeArray parameters)

    
    //NativeModules是一个接口,我们知道要实现供JS调用的Java API我们需要编写类继承BaseJavaModule/ReactContextBaseJavaModule,BaseJavaModule/ReactContextBaseJavaModule就
    //实现了NativeModule接口,
     private final ArrayList<NativeModule.NativeMethod> mMethods;
    
    @DoNotStrip
    public class JavaModuleWrapper {
      @DoNotStrip
      public void invoke(ExecutorToken token, int methodId, ReadableNativeArray parameters) {
        if (mMethods == null || methodId >= mMethods.size()) {
          return;
        }
        //mMethods为所有继承BaseJavaModule类的BaseJavaModule.JavaMethod对象,也就是被ReactMethod注解标记的方法。
        mMethods.get(methodId).invoke(mJSInstance, token, parameters);
      }
    }

    JavaModuleWrapper对应C++层的NativeModule,该类针对Java BaseJavaModule进行了包装,是的C++层可以更加方便的通过JNI调用Java Module。

    四 Java层调用JS层

    RN应用通信机制流程图(Java->JS)如下所示:

    举例

    在文章ReactNative源码篇:启动流程中,我们在ReactInstanceManager.onAttachedToReactInstance()方法中调用APPRegistry.jS的runApplication()来
    启动RN应用,这就是一个典型的Java层调用JS层的例子,我们来具体分析一下这个例子的实现方式。

    1 首先定义了接口AppRegistry,该接口继承于JavaScriptModule,如下所示:

    /**
     * JS module interface - main entry point for launching React application for a given key.
     */
    public interface AppRegistry extends JavaScriptModule {
    
      void runApplication(String appKey, WritableMap appParameters);
      void unmountApplicationComponentAtRootTag(int rootNodeTag);
      void startHeadlessTask(int taskId, String taskKey, WritableMap data);
    }

    2 然后在CoreModulesPackage.createJSModules()将它添加到JavaScriptModule列表中,这个列表最终会被添加到JavaScriptModuleRegistry中。

    class CoreModulesPackage extends LazyReactPackage implements ReactPackageLogger {
    
      @Override
      public List<Class<? extends JavaScriptModule>> createJSModules() {
        List<Class<? extends JavaScriptModule>> jsModules = new ArrayList<>(Arrays.asList(
            DeviceEventManagerModule.RCTDeviceEventEmitter.class,
            JSTimersExecution.class,
            RCTEventEmitter.class,
            RCTNativeAppEventEmitter.class,
            AppRegistry.class,
            com.facebook.react.bridge.Systrace.class,
            HMRClient.class));
    
        if (ReactBuildConfig.DEBUG) {
          jsModules.add(DebugComponentOwnershipModule.RCTDebugComponentOwnership.class);
          jsModules.add(JSCHeapCapture.HeapCapture.class);
          jsModules.add(JSCSamplingProfiler.SamplingProfiler.class);
        }
    
        return jsModules;
      }
    }

    3 通过Java层调用AppRegistry.js的runApplication()方法,如下所示:

    //启动流程入口:由Java层调用启动
    catalystInstance.getJSModule(AppRegistry.class).runApplication(jsAppModuleName, appParams);

    Java层调用JS层实现流程:

    Java层

    1 把要实现的功能编写成接口并继承JavaScriptModule,并交由ReactPackage管理,最终会在RN初始化的时候添加到JavaScriptModuleRegistry注册表中。
    2 通过ReactContext或者CatalystInstanceImpl获取JavaScriptModule,它们最终会通过JavaScriptModuleRegistry.getJavaScriptModule()获取对应的JavaScriptModule。
    3 JavaScriptModuleRegistry通过动态代理生成对应的JavaScriptModule,然后通过invoke()调用相应的JS方法,该方法会进一步去调用CatalystInstanceImpl.callJSFunction()
    该方法会调用native方法CatalystInstanceImpl.jniCallJSFunction()方法将相关参数传递到C++层,至此,整个流程便转入C++层。

    C++层

    4 CatalystInstanceImpl在C++层对应的是类CatalystInstanceImpl.cpp。CatalystInstanceImpl.cpp是RN针对Android平台的包装类,具体功能由Instance.cpp来完成,
    即Instance.cpp的callJSFunction()方法。
    5 Instance.cpp的callJSFunction()方法按照调用链:Instance.callJSFunction()->NativeToJsBridge.callFunction()->JSCExecutor.callFunction()最终将
    功能交由JSCExecutor.cpp的callFunction()方法来完成。
    6 JSCExecutor.cpp的callFunction()方法通过Webkit JSC调用JS层的MessageQueue.js里的callFunctionReturnFlushedQueue()方法,自此整个流程转入JavaScript层。

    JS层

    7 MessageQueue.js里的callFunctionReturnFlushedQueue()方法,该方法按照调用链:MessageQueue.callFunctionReturnFlushedQueue()->MessageQueue.__callFunction()
    在JS层里的JavaScriptModule注册表里产找对应的JavaScriptModule及方法。

    我们来分析上述代码的调用方式。

    可以看出AppRegistry继承于JavaScriptModule,AppRegistry作为核心逻辑之一被添加到CoreModulesPackage中,我们知道在ReactInstanceManager.createReactContext()方法
    中,CoreModulesPackage作为ReactPackage被添加进了JavaScriptModuleRegistry中,JavaScriptModuleRegistry被CatalystInstanceImpl来管理。

    所以才有了Java层调用JS层代码的通用格式:

    CatalystInstanceImpl.getJSModule(xxx.class).method(params, params, ...);

    当然,如果使我们调用自己的JS Module,我们是用ReactContext.getJSModule(),因为ReactContext持有CatalystInstanceImpl的实例,CatalystInstanceImpl并不直接对外公开。

    Java层代码调用JS层代码,需要将JavaScriptModule注册到JavaScriptModuleRegistry中,然后通过动态代理获取方法的各种参数,再将参数通过参数通过C++层传递到JS层从而完成调用,我们
    先来看看CatalystInstanceImpl是如何拿到JavaScriptModule的。

    CatalystInstanceImpl.getJSModule()调用JavaScriptModuleRegistry.getJavaScriptModule()去查询JavaScriptModule。

    4.1 JavaScriptModuleRegistry.getJavaScriptModule(CatalystInstance instance, ExecutorToken executorToken, Class moduleInterface)

    它的实现如下所示:

    public class JavaScriptModuleRegistry {
    
      public synchronized <T extends JavaScriptModule> T getJavaScriptModule(
        CatalystInstance instance,
        ExecutorToken executorToken,
        Class<T> moduleInterface) {
    
        //如果JavaScriptModule加载一次,就保存在缓存中,第二次加载时直接从缓存中取。
        HashMap<Class<? extends JavaScriptModule>, JavaScriptModule> instancesForContext =
            mModuleInstances.get(executorToken);
        if (instancesForContext == null) {
          instancesForContext = new HashMap<>();
          mModuleInstances.put(executorToken, instancesForContext);
        }
        JavaScriptModule module = instancesForContext.get(moduleInterface);
        if (module != null) {
          return (T) module;
        }
    
        //利用动态代理获取JavaScriptModule对象
    
        //JavaScriptModuleRegistration是对JavaScriptModule的包装,检查实现JavaScriptModule接口的类是否存在重载,因为JS不支持重载。
        JavaScriptModuleRegistration registration =
            Assertions.assertNotNull(
                mModuleRegistrations.get(moduleInterface),
                "JS module " + moduleInterface.getSimpleName() + " hasn't been registered!");
        JavaScriptModule interfaceProxy = (JavaScriptModule) Proxy.newProxyInstance(
            moduleInterface.getClassLoader(),
            new Class[]{moduleInterface},
            new JavaScriptModuleInvocationHandler(executorToken, instance, registration));
        instancesForContext.put(moduleInterface, interfaceProxy);
        return (T) interfaceProxy;
      }
    
      private static class JavaScriptModuleInvocationHandler implements InvocationHandler {
    
        private final WeakReference<ExecutorToken> mExecutorToken;
        private final CatalystInstance mCatalystInstance;
        private final JavaScriptModuleRegistration mModuleRegistration;
    
        public JavaScriptModuleInvocationHandler(
            ExecutorToken executorToken,
            CatalystInstance catalystInstance,
            JavaScriptModuleRegistration moduleRegistration) {
          mExecutorToken = new WeakReference<>(executorToken);
          mCatalystInstance = catalystInstance;
          mModuleRegistration = moduleRegistration;
        }
    
        @Override
        public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args) throws Throwable {
          ExecutorToken executorToken = mExecutorToken.get();
          if (executorToken == null) {
            FLog.w(ReactConstants.TAG, "Dropping JS call, ExecutorToken went away...");
            return null;
          }
          NativeArray jsArgs = args != null ? Arguments.fromJavaArgs(args) : new WritableNativeArray();
          //调用CatalystInstanceImpl.callFunction()方法。
          mCatalystInstance.callFunction()方法。(
            executorToken,
            mModuleRegistration.getName(),
            method.getName(),
            jsArgs
          );
          return null;
        }
      }
    }

    可以看出,在JavaScriptModuleRegistry通过动态代理的方式获取JavaScriptModule,对Java动态代理不熟悉的同学,这里我们先简单回忆一下Java动态代理相关内容。

    Java动态代理

    Java动态代理主要涉及两个类:
    
    java.lang.reflect.Proxy:用来生成代理类。
    java.lang.reflect.InvocationHandler:调用处理器,我们需要自己定义一个类来指定动态生成的代理类需要完成的具体内容。
    
    Proxy的主要方法:
    
    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler handler)//创建代理对象  
    
    ClassLoader loader:类加载器,指定使用哪个类加载器将代理类加载到JVM的方法区。
    Class<?>[] interfaces:代理类需要实现的接口。
    InvocationHandler handler:调用处理器实例,指定代理类具体要做什么。
    
    实现Java动态代理需要以下3步:
    
    1 定义一个委托类和公共接口。
    2 定义调用处理器类实现InvocationHandler接口,指定代理类具体要完成的任务。
    3 生成代理对象
    
    一个代理对象对应一个委托类对应一个调用处理器类

    JavaScriptModuleInvocationHandler.invoke()方法获取了moduleID,methodID,并去调用CatalystInstanceImpl.callFunction();

    4.2 CatalystInstanceImpl.callFunction(ExecutorToken executorToken, final String module, final String method, final NativeArray arguments)

    
    public class CatalystInstanceImpl{
    
      @Override
      public void callFunction(
          ExecutorToken executorToken,
          final String module,
          final String method,
          final NativeArray arguments) {
        if (mDestroyed) {
          FLog.w(ReactConstants.TAG, "Calling JS function after bridge has been destroyed.");
          return;
        }
        if (!mAcceptCalls) {
          // Most of the time the instance is initialized and we don't need to acquire the lock
          synchronized (mJSCallsPendingInitLock) {
            if (!mAcceptCalls) {
              mJSCallsPendingInit.add(new PendingJSCall(executorToken, module, method, arguments));
              return;
            }
          }
        }
    
        jniCallJSFunction(executorToken, module, method, arguments);
      }
    
      private native void jniCallJSCallback(ExecutorToken executorToken, int callbackID, NativeArray arguments);
    }

    方法走到这里,实现逻辑已经由Java层转到C++层,我们去C++层看看具体的实现。

    CatalystInstanceImpl.java在C++层有个对应的类CatalystInstanceImpl.cpp。

    4.3 CatalystInstanceImpl.jniCallJSFunction(JExecutorToken token, std::string module, std::string method, NativeArray arguments)

    CatalystInstanceImpl.cpp

    void CatalystInstanceImpl::jniCallJSFunction(
    
        JExecutorToken* token, std::string module, std::string method, NativeArray* arguments) {
      // We want to share the C++ code, and on iOS, modules pass module/method
      // names as strings all the way through to JS, and there's no way to do
      // string -> id mapping on the objc side.  So on Android, we convert the
      // number to a string, here which gets passed as-is to JS.  There, they they
      // used as ids if isFinite(), which handles this case, and looked up as
      // strings otherwise.  Eventually, we'll probably want to modify the stack
      // from the JS proxy through here to use strings, too.
      instance_->callJSFunction(token->getExecutorToken(nullptr),
                                std::move(module),
                                std::move(method),
                                arguments->consume());
    }

    可以发现CatalystInstanceImpl.cpp的jniCallJSFunction()方法又会去调用Instance.cpp里的callJSFunction()方法,其实CatalystInstanceImpl.cpp只是RN针对
    Android平台适配的封装,主要做了写参数类型转换,本质上它对应了ReactCommon包里的Instance.cpp,真正的实现在Instance.cpp中完成。我们来看一看Instance.cpp中的实现。

    4.4 Instance.callJSFunction(ExecutorToken token, std::string&& module, std::string&& method, folly::dynamic&& params)

    Instance.cpp

    void Instance::callJSFunction(ExecutorToken token, std::string&& module, std::string&& method,
                                  folly::dynamic&& params) {
      callback_->incrementPendingJSCalls();
      nativeToJsBridge_->callFunction(token, std::move(module), std::move(method), std::move(params));
    }

    Instance.cpp的callJSFunction()进一步去调用NativeToJsBridge.cpp的callFunction()方法,我们来看看它的实现。

    4.5 NativeToJsBridge.callFunction(ExecutorToken executorToken, std::string&& module, std::string&& method, folly::dynamic&& arguments)

    NativeToJsBridge.cpp

    void NativeToJsBridge::callFunction(
        ExecutorToken executorToken,
        std::string&& module,
        std::string&& method,
        folly::dynamic&& arguments) {
    
      int systraceCookie = -1;
      #ifdef WITH_FBSYSTRACE
      systraceCookie = m_systraceCookie++;
      std::string tracingName = fbsystrace_is_tracing(TRACE_TAG_REACT_CXX_BRIDGE) ?
        folly::to<std::string>("JSCall__", module, '_', method) : std::string();
      SystraceSection s(tracingName.c_str());
      FbSystraceAsyncFlow::begin(
          TRACE_TAG_REACT_CXX_BRIDGE,
          tracingName.c_str(),
          systraceCookie);
      #else
      std::string tracingName;
      #endif
    
      runOnExecutorQueue(executorToken, [module = std::move(module), method = std::move(method), arguments = std::move(arguments), tracingName = std::move(tracingName), systraceCookie] (JSExecutor* executor) {
        #ifdef WITH_FBSYSTRACE
        FbSystraceAsyncFlow::end(
            TRACE_TAG_REACT_CXX_BRIDGE,
            tracingName.c_str(),
            systraceCookie);
        SystraceSection s(tracingName.c_str());
        #endif
    
        //调用JSCExecutor.cppd的callFunction()
        // This is safe because we are running on the executor's thread: it won't
        // destruct until after it's been unregistered (which we check above) and
        // that will happen on this thread
        executor->callFunction(module, method, arguments);
      });
    }

    NativeToJsBridge.cpp的callFunction()进一步去调用JSCExecutor.cppd的callFunction()方法,我们来看看它的实现。

    4.6 JSCExecutor.callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments)

    JSCExecutor.cpp

    void JSCExecutor::callFunction(const std::string& moduleId, const std::string& methodId, const folly::dynamic& arguments) {
      SystraceSection s("JSCExecutor::callFunction");
      // This weird pattern is because Value is not default constructible.
      // The lambda is inlined, so there's no overhead.
    
      auto result = [&] {
        try {
          // See flush()
          CHECK(m_callFunctionReturnFlushedQueueJS)
            << "Attempting to call native methods without loading BatchedBridge.js";
          return m_callFunctionReturnFlushedQueueJS->callAsFunction({
            Value(m_context, String::createExpectingAscii(m_context, moduleId)),
            Value(m_context, String::createExpectingAscii(m_context, methodId)),
            Value::fromDynamic(m_context, std::move(arguments))
          });
        } catch (...) {
          std::throw_with_nested(
            std::runtime_error("Error calling " + moduleId + "." + methodId));
        }
      }();
    
      //将调用结果返回给Java层
      callNativeModules(std::move(result));
    }

    可以看出,该函数进一步调用m_callFunctionReturnFlushedQueueJS->callAsFunction(),我们先来解释下m_callFunctionReturnFlushedQueueJS这个变量的由来,它在JSCExecutor::bindBridge()里
    初始化,本质上就是通过Webkit JSC拿到JS层代码相关对象和方法引用,m_callFunctionReturnFlushedQueueJS就是MessageQueue.js里的callFunctionReturnFlushedQueue()方法的引用。

    void JSCExecutor::bindBridge() throw(JSException) {
    
      ...
    
     m_callFunctionReturnFlushedQueueJS = batchedBridge.getProperty("callFunctionReturnFlushedQueue").asObject();
    
      ...
    }

    4.7 MessageQueue.callFunctionReturnFlushedQueue(module: string, method: string, args: Array)

    MessageQueue.callFunctionReturnFlushedQueue()方法的实现如下所示:

    MessageQueue.js

      callFunctionReturnFlushedQueue(module: string, method: string, args: Array<any>) {
        guard(() => {
          this.__callFunction(module, method, args);
          this.__callImmediates();
        });
    
        return this.flushedQueue();
      }
    
      __callFunction(module: string, method: string, args: Array<any>) {
        this._lastFlush = new Date().getTime();
        this._eventLoopStartTime = this._lastFlush;
        Systrace.beginEvent(`${module}.${method}()`);
        if (this.__spy) {
          this.__spy({ type: TO_JS, module, method, args});
        }
    
        //从JS层的JavaScriptModule注册表中查找到AppRegistry.js
        const moduleMethods = this._callableModules[module];
        invariant(
          !!moduleMethods,
          'Module %s is not a registered callable module (calling %s)',
          module, method
        );
        invariant(
          !!moduleMethods[method],
          'Method %s does not exist on module %s',
          method, module
        );
        //取到Java层调用的JS层方法,例如:AppRegistry.js的runApplication()方法
        const result = moduleMethods[method].apply(moduleMethods, args);
        Systrace.endEvent();
        return result;
      }

    从JS层的JavaScriptModule注册表中查找到AppRegistry.js,最终调用AppRegistry.js的runApplication()方法。

    好,以上就是Java层代码调用JS层代码的全部流程,我们再来总结一下:

    Java层

    1 把要实现的功能编写成接口并继承JavaScriptModule,并交由ReactPackage管理,最终会在RN初始化的时候添加到JavaScriptModuleRegistry注册表中。
    2 通过ReactContext或者CatalystInstanceImpl获取JavaScriptModule,它们最终会通过JavaScriptModuleRegistry.getJavaScriptModule()获取对应的JavaScriptModule。
    3 JavaScriptModuleRegistry通过动态代理生成对应的JavaScriptModule,然后通过invoke()调用相应的JS方法,该方法会进一步去调用CatalystInstanceImpl.callJSFunction()
    该方法会调用native方法CatalystInstanceImpl.jniCallJSFunction()方法将相关参数传递到C++层,至此,整个流程便转入C++层。

    C++层

    4 CatalystInstanceImpl在C++层对应的是类CatalystInstanceImpl.cpp。CatalystInstanceImpl.cpp是RN针对Android平台的包装类,具体功能由Instance.cpp来完成,
    即Instance.cpp的callJSFunction()方法。
    5 Instance.cpp的callJSFunction()方法按照调用链:Instance.callJSFunction()->NativeToJsBridge.callFunction()->JSCExecutor.callFunction()最终将
    功能交由JSCExecutor.cpp的callFunction()方法来完成。
    6 JSCExecutor.cpp的callFunction()方法通过Webkit JSC调用JS层的MessageQueue.js里的callFunctionReturnFlushedQueue()方法,自此整个流程转入JavaScript层。

    JavaScript层

    7 MessageQueue.js里的callFunctionReturnFlushedQueue()方法,该方法按照调用链:MessageQueue.callFunctionReturnFlushedQueue()->MessageQueue.__callFunction()
    在JS层里的JavaScriptModule注册表里产找对应的JavaScriptModule及方法。
    展开全文
  • 当第三方插件源码修改后,必须重启服务. 猜测,第三方插件在服务启动时会生成缓存. 如果不重启无论如何修改代码,都不会产生影响——这个是经过测试的 注意是重启服务,不是简单的cmd+R 刷新,也不是关闭应用重新打开,...
    当第三方插件源码修改后,必须重启服务.
    猜测,第三方插件在服务启动时会生成缓存.
    如果不重启无论如何修改代码,都不会产生影响——这个是经过测试的
    注意是重启服务,不是简单的cmd+R 刷新,也不是关闭应用重新打开,也不是点击XCode的按钮执行重新安装.
    必须npm start 或者react-native run-ios  或者关闭终端后在XCode中点击按钮重新安装 
    展开全文
  • 经过了一番对ReactNative的学习之后,我们都知道只要敲入react-native run-android神奇的JS代码就被编译成Android可以理解的View了,不得不惊叹ReactNative隐藏的神秘的力量,所以本文分析一下一个Hello,World的JS...
  • react-native 修改源码后,如何让其他同事直接能用,每次都发过去修改的node源文件肯定是不方便的,这里分享个脚本 其实我们需要达成的目的是修改NodaModule l的源码其他人也在,但是NodaModule 文件肯定是忽略的...
  • 今天就从源码的角度来分析下React-Native底层的通信机制。了解下底层是如何通信的对开发也有所好处。概要先大概讲一下React-Native的通信过程。RN主要的通信在于java与js之间,平常我们写的jsx代码最终会调用到原生...
  • ReactNative源码篇:渲染原理关于作者 郭孝星,非著名程序员,主要从事Android平台基础架构与中间件方面的工作,欢迎交流技术方面的问题,可以去我的Github提交Issue或者发邮件至guoxiaoxingse@163.com与我联系。 ...
  • 理解 RN 框架的一些东西,以便裁剪和对 RN 有个更深入的认识,所以本篇总结了我这段时间阅读源码的一些感触,主要总结了 React Native 启动流程、JS 调用 Java 流程、Java 调用 JS 流程。
  • 1ReactNative源码篇:源码初识2ReactNative源码篇:代码调用3ReactNative源码篇:启动流程4ReactNative源码篇:渲染原理5ReactNative源码篇:线程模型6ReactNative源码篇:通信机制 源码地址:ht
  • 本文开始分析f8app核心js部分的源码,这篇文章...React Native的理念是Learn once,write anywhere, Android和iOS App端的js代码是放在一起的,以便最大限度的复用业务逻辑,UI部分的可以根据平台特性各自实现,React n
  • 近期和一些朋友聊到了 React-Native 的官方重构状态,而刚好近期发布的 0.59.x 系列版本中,上层设计出现了比较大的调整,结合体验之后的状态,就想聊聊 React-Native 的现状、新版本的升级体验、还有新支持的 React...
  • React-Native项目源码

    2020-06-02 23:32:52
    react native开发的厕所在哪的源代码,初学者可以看看
  •   ReactNative作为使用React开发Native应用的新框架,随着时间的增加,无论是社区还是...此文目的是希望和大家一起欣赏一下ReactNative的部分源码。阅读源码好处多多,让攻城狮更溜的开发ReactNative应用的同时,也
  • React Native常用第三方组件汇总见链接: http://blog.csdn.net/chichengjunma/article/details/52920137点击打开链接 有时候第三方组件从样式上并不能如我们意愿,那我们如何更改React Native第三方组件的样式呢...
  • 原生分析的是安卓端 ...//react-native/vendor/core/wehatwg-fetch.js self.fetch = function(input, init) { return new Promise(function(resolve, reject) { var request = new Request(input, init); v...
  • React源码阅读:概况

    2019-02-28 09:11:20
    前言 本文主要介绍一些React的设计思想和相关...React源码阅读:虚拟DOM的初始化 项目结构 React的相关代码都放在packages里。 ├── packages ------------------------------- React实现的相关代码 │ ├── ...
  • 阅读源码时,有许多变量在程序运行过程中不断的产生,其中存放着什么东西,一直是一个比较头疼的问题。不停的推导增加了验算的负担,随着代码逐渐的深入,也会产生一定的记忆负担。如果靠脑袋去记,简单点的代码还好...
  • mac端可视化调试react或者reactnative工具,可以调试redux应用状态
1 2 3 4 5 ... 20
收藏数 10,558
精华内容 4,223
关键字:

native react 源码修改