jsbridge实现及原理_【React Native for Android】jsBridge实现原理

本文详细解析了React Native for Android中JSBridge的初始化过程和模块注册,包括Native模块与JavaScript模块的注册、CatalystInstance的创建、ReactBridge的初始化,以及Java如何执行JavaScript等核心流程。
摘要由CSDN通过智能技术生成

基于源码版本:0.28

简单整理了一下几个组件之间的关系,

1.JSBridge初始化过程

React Native for Android(RN4A)的核心流程在QZone的框架启动核心路径剖析一文中讲述得很详细,本文不再赘述,主要解析RN4A里面的Native&JS通信机制。

注:Java在RN4A中是Native模块,涉及JNI的部分在java中术语为native,注意大小写的区分不要混淆。

在ReactInstanceManager初始化时会创建ReactContext,其中主要的一部分工作就是注册Native&js模块,我们看看它都做了什么:

1.1模块注册

在createReactContext()中会先注册CoreModulesPackage的Native&JS模块与所有的ViewManager,之后注册在ReactNativeHost中(0.28之前的版本在ReactActivity中)声明的所有其他ReactPackage的Native&JS模块,其中:

o    Native模块注册

将所有的NativeModule添加进NativeModuleRegistry.Builder后,它会依次build每一个NativeModule,主要做的事情就是生成moduleID、解析带@ReactMethod注解的方法;

o    JavaScript模块注册

o    在js层,js模块在写的时候都需要加上BatchedBridge.registerCallableModule('module', Module);注册到BatchedBridge.js中以供后续查找;

o    在java层,js模块将想要暴露出来的方法声明为一个接口类,它被build的时候利用Java动态代理生成实例, 具体的方法invoke由CatalystInstance.callFunction代理执行。

注册完后,会初始化CatalystInstance, 模块注册、build生成后的NativeModuleRegistry与JavaScriptModuleRegistry都由CatalystInstance持有.

1.2初始化ReactBridge

CatalystInstance初始化时会初始化ReactBridge,ReactBridge是在Java层与js沟通的桥梁(废话..),它是一个native类,大部分实现位于Bridge.h/.cpp,在初始化时调用的native initialize()方法对应OnLoad.cpp中的create函数,它的需要三个参数:

o    JavaScriptExecutor

RN4A使用WebKit的JavaScriptCore(JSCore)来解析js, JavaScriptExecutor的实现类JSCJavaScriptExecutor是一个native类,它封装了JSCore的逻辑(对应JSCExecutor.h/.cpp)。

o    ReactCallback

为JavaScript提供Java module执行入口。

o    MessageQueueThread

包装着Java模块执行的线程Handler。 注: RN4A里面有三个主要执行线程:UI线程, JS线程, Java线程.

我们看一下它的初始化过程:1

2

3

4

5

6

7

8

9

10static void create(JNIEnv* env, jobject obj, jobject   executor, jobject callback,

jobject   callbackQueueThread) {

auto weakCallback = createNew(callback);

auto weakCallbackQueueThread =   createNew(callbackQueueThread);

auto bridgeCallback =   folly::make_unique(weakCallback, weakCallbackQueueThread);

auto nativeExecutorFactory =   extractRefPtr(env, executor);

auto executorTokenFactory =   folly::make_unique();

auto bridge = createNew(nativeExecutorFactory.get(), std::move(executorTokenFactory), std::move(bridgeCallback));

setCountableForJava(env, obj, std::move(bridge));

}

实际上做的工作也不多,就是一堆封装,最后生成了一个继承Countable的Bridge实例,继承Countable的对象的内存是由native分配的,也由native回收。

CatalystInstance初始化完ReactBridge后, 创建NativeModuleRegistry的JSON至并放在javascript的全局变量global.__fbBatchedBridgeConfig中:1

2

3bridge.setGlobalVariable(

"__fbBatchedBridgeConfig",

buildModulesConfigJSONProperty(mJavaRegistry));

在buildModulesConfigJSONProperty函数中我们可以看出Native模块打包出的json数据结构大概为:1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18remoteModuleConfig:   {

moduleName : {

moduleId: 0,

supportsWebWorkers: false,

methods: {

methodName : {

methodId: 0,

methodType: remote

}

// other methods ...

},

constants: {

constant1: 123,

constant2: 234

}

},

// other modules ...

}

这里的bridge.setGlobalVariable最后到JSCExecutor.setGlobalVariable中,通过JavaScriptCore::JSObjectSetProperty实现。

JS加载Java模块

在Java模块被封装好注入到gloabl.__fbBatchedBridgeConfig后,js需要进行一些处理,这里有三个模块需要注意:NativeModule.js, BatchedBridge.js, MessageQueue.js,我们来看看RN4A是怎么一步步载入Native模块的:

1.    js模块中我们都会require('react-native')这样(或者ES6的import xxx from ('react-native')),此时加载的react-native.js模块中,会加载NativeModule.js模块,NativeModule.js又会去加载BatchedBridge.js,在这里解析之前传入的global.__fbBatchedBridgeConfig;

2.    BatchedBridge.js里面封装了MessageQueue的一个实例,它将global.__fbBatchedBridgeConfig传给了MessageQueue的构造函数,我们看看构造函数里对它干了什么:1

2

3

4

5

6

7

8

9lazyProperty(this, 'RemoteModules', () => {

const {remoteModuleConfig} = configProvider();

const modulesConfig = this._genModulesConfig(remoteModuleConfig);

const modules = this._genModules(modulesConfig);

//生成调试用的Native module/method查找表

return modules;

});

这段代码还算浅显易懂,它定义了一个RemoteModules属性,其中将模块、方法、常量什么的解析为js可用的变量、函数。lazyProperty函数实际上就是Object.defineProperty函数里面对property指定get(),不直接初始化。

再加上之前所说的,需要暴露给java的JS模块都会调用BatchedBridge.registerCallableModule来注册自己,最后也会跑到MessageQueue里面,这样一来,所有的Native&JS模块入口都集中在了MessageQueue里面。BatchedBridge最后将自己定义为了一个全局变量,方便JSCore直接找到它:1Object.defineProperty(global, '__fbBatchedBridge', { value: BatchedBridge });

1.    加载好BatchedBridge.js后,回到NativeModule.js, 它BatchedBridge.RemoteModules又做了一点处理:1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26const NativeModules = {};

Object.keys(RemoteModules).forEach((moduleName)   => {

Object.defineProperty(NativeModules, moduleName, {

configurable: true,

enumerable: true,

get: () => {

let module = RemoteModules[moduleName];

//IOS用,如果注入了nativeRequireModuleConfig方法,则由它生成moduleConfig

if (module && typeof module.moduleID === 'number' &&   global.nativeRequireModuleConfig) {

const json =   global.nativeRequireModuleConfig(moduleName);

const config = json && JSON.parse(json);

module = config &&   BatchedBridge.processModuleConfig(config, module.moduleID);

RemoteModules[moduleName] = module;

}

//将每个生成的module结构定义为自己的变量

Object.defineProperty(NativeModules,   moduleName, {

configurable: true,

enumerable: true,

value: module,

});

return module;

},

});

});

于是我们就会看到RN4A官方文档上所说的,如果你定义了一个Native模块,需要额外加一个文件,声明如下信息:1

2import { NativeModules } from 'react-native';

module.exports =   NativeModules.YourNativeModule;

这下就明白了,因为所有Native的模块都会在加载的时候注册到NativeModules里面。

Java执行JS

上面说到所有的JavaScript模块的方法都通过动态代理交给CatalystInstance.callFunction来执行,那么具体是怎么被执行的呢,我们来看看在JSCExecutor.cpp这一层,它会生成JS调用gloabl.__fbBatchedBridge.callFunctionReturnFlushedQueue.apply(null, [module, method, args])交由JavascriptCore来执行。结合上文分析,这个gloabl.__fbBatchedBridge就是MessageQueue。

MessageQueue.callFunctionReturnFlushedQueue里面直接调用了__callFunction()函数,我们看看它是怎么找JS模块执行的:1

2

3

4

5

6

7__callFunction(module: string, method: string, args: any) {

//标记时间, 开始systrace

const moduleMethods = this._callableModules[module];

//检查module合法性

moduleMethods[method].apply(moduleMethods,   args);

//结束systrace

}

this._callableModules里面就存放了所有由BatchedBridge.registerCallableModule注册上来的java模块,然后再找对应方法执行即可。

关于js如何执行java的,请看下文分解吧。大头鬼的ReactNativeAndroid源码分析-Js如何调用Native的代码写这块很不错,大家也可以作为参考。

由于RN只是一个View层的框架,还需要配合Flux或者Redux才能进行项目开发,并且JSX这样的语法与往常的Web、Native开发语法习惯都比较不同,它的学习曲线非常高。我写了一个配合Flow&ES6的todo-example,可以供初学者学习:)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值