IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 【React Native】深入理解Native与RN通信原理 -> 正文阅读

[移动开发]【React Native】深入理解Native与RN通信原理

? ? ? ? 在使用 React Native 开发应用程序的时候,有时候需要使用?JavaScript 中默认不可用的 IOS 或 Android 的原生 API。 也许你想复用一些现有的 OC、Swift、Java 或 C++ 库,而不必在 JavaScript 中重新实现它,或者为图像处理之类的事情编写一些高性能、多线程的代码。那么此时就不得不与Native打交道了。

? ? ? ? 幸运的是,React Native已经提供了这样的能力来供你使用,在JS Core的强大支持下,我们RN侧可以使用NativeModule 来将Native代码作为 JS 对象暴露给 JavaScript来调用,从而允许您从 JS 内可以调用Native代码。

? ? ? ? 作为非原生开发,虽然我们不希望此功能成为通常开发过程的一部分,但它的存在至关重要。 并且如果 React Native 没有导出你的应用程序需要的原生 API,那么你应该能够自己封装并且导出它!

下面,我们来深入聊一聊RN的初始化阶段,以及原生端与JS端互相通信的过程。

JS Core

????????开始之前,我觉得有必要简单介绍一下JS Core,众所周知,RN是通过JavaScriptCore提供的能力来与native交互的,JavaScript Core(简称 JSCore)是一个开源的框架,是?WebKit?的一部分,用最简单的话描述这个框架,它大概提供了两种能力:

  1. 在Native的环境下执行JS代码,不需要浏览器或Node的环境。
  2. 把原生代码注入到JS中,提供给JS调用原生代码的能力。

????????JSExport 是整个 JSCore 里面最神奇的部分,也正是有了 JSExport 才让我们把 Native 对象暴露给 JS 环境非常的容易,简单说,通过JSExport我们可以把 Native 对象的属性和方法暴露给 JS 环境,先来看一段OC的接口代码:

@protocol NativeObjectExport <JSExport>

@interface NativeObject : NSObject<NativeObjectExport>
@property (nonatomic, assign) BOOL property1;
@property (nonatomic, strong) id property2;

- (void)method1:(JSValue *)arguments;
- (void)method2;
@end

????????NativeObject 可以被实现为任意的对象,只要他实现了NativeObjectExport那么这个协议里面的属性和对象就可以直接被 JS 环境使用,例如上面的 property1 和 method1。我们只要在 context 里面注入一个NativeObject的对象,就可以在 JS 环境放肆的与 Native 进行交互了:

context[@"helper"] = [NativeObject new];
[arguments[@"handler"] callWithArguments:@[object]];

在JS的环境中

// 可以通过property1访问native
var prop = helper.property1;
// 可以通过method1调用native的方法
helper.method1({
  handler: function(object) {}
});

????????当Native代码执行完 method1之后,可以通过这个 handler 回调到 JS 环境,JS 环境通过 function 的 object 拿到返回结果,这就是一个完整的流程。

初始化阶段

? ? ? ? 在RN初始化阶段,首先原生端会遍历开发者自定义的原生模块与RN框架提供的原生模块,将其注册到一张原生模块映射表中,然后,原生端也会将需要调用的JS模块注册到一张JS模块映射表中,需要注意的是,原生端并没有实现JS Module,只是有一份接口而已(Android需要实现JS接口,但是IOS不需要)。

? ? ? ? 紧接着JS Core会将两份映射表传入到JS侧,在JS侧,原生模块映射表会绑定到RN的提供的NativeModule上面,这样JS就可以通过NativeModule来调用Native提供的API了,对于JS模块映射表来说,JS侧会实现对应的JS方法,并注册进去。这样,Native就可以调用JS提供的方法了。并且JS可以以回调参数的形式来接受Native传来的数据。

请看源码:

import { NativeModules } from 'react-native';

const _ActionSheet = {
  showActionSheet: createNoop('ActionSheet.showActionSheet'),
  ...NativeModules.GAActionSheet,
};

????????这是一段从Native SDK里拿出来的源码,可以看到,GAActionSheet就是native暴露的api,createNoop方法只是为了在native api还没加载完成时就调用时的一种兜底方案。

const ViewEventEmitter = new NativeEventEmitter(NativeModules.GAViewEventEmitter)

ViewEventEmitter.addListener(self_callback);

? ? ? ? 相应地,上述代码展示了怎样通过ReactNative提供的NativeEventEmitter来订阅一个JS事件。

????????附一段react native源码来解释NativeModules到底是什么,由于React Native源码较多,已被我删减了很多,感兴趣的同学可以去react-native/Libraries/BatchedBridge/NativeModules.js底下查看,可以看到,NativeModules正是所谓的“原生模块映射表”。

? ? ? ? 另外,可以看到,通过global全局对象可以注册JS函数供Native调用,JS可以通过global来获取Native传递过来的数据。

function genModule(
  config: ?ModuleConfig,
  moduleID: number,
): ?{
  name: string,
  module?: Object,
} {
  const [moduleName, constants, methods, promiseMethods, syncMethods] = config;
  const module = {};
  methods &&
    methods.forEach((methodName, methodID) => {
    ...
    });
  return {name: moduleName, module};
}

global.__fbGenNativeModule = genModule;

let NativeModules: {[moduleName: string]: Object, ...} = {};
if (global.nativeModuleProxy) {
  NativeModules = global.nativeModuleProxy;
} else if (!global.nativeExtensions) {
  const bridgeConfig = global.__fbBatchedBridgeConfig;
  (bridgeConfig.remoteModuleConfig || []).forEach(
    (config: ModuleConfig, moduleID: number) => {
      const info = genModule(config, moduleID);
      if (!info) {
        return;
      }
      if (info.module) {
        NativeModules[info.name] = info.module;
      }
    },
  );
}

????????这里展示了JSCore中如何获取并执行JS中注册好的global.__fbGenNativeModule = genModule

folly::Optional<Object> JSINativeModules::createModule(
    Runtime &rt,
    const std::string &name) {
  bool hasLogger(ReactMarker::logTaggedMarker);
  if (hasLogger) {
    ReactMarker::logTaggedMarker(
        ReactMarker::NATIVE_MODULE_SETUP_START, name.c_str());
  }

  if (!m_genNativeModuleJS) {
    m_genNativeModuleJS =
        rt.global().getPropertyAsFunction(rt, "__fbGenNativeModule");
  }

  auto result = m_moduleRegistry->getConfig(name);
  if (!result.hasValue()) {
    return folly::none;
  }

  Value moduleInfo = m_genNativeModuleJS->call(
      rt,
      valueFromDynamic(rt, result->config),
      static_cast<double>(result->index));
  CHECK(!moduleInfo.isNull()) << "Module returned from genNativeModule is null";

  folly::Optional<Object> module(
      moduleInfo.asObject(rt).getPropertyAsObject(rt, "module"));

  if (hasLogger) {
    ReactMarker::logTaggedMarker(
        ReactMarker::NATIVE_MODULE_SETUP_STOP, name.c_str());
  }

  return module;
}

原生端调用JS

? ? ? ? ?接下来看一下原生端怎样调用JS,首先,Native在初始化时候创建好的JS模块映射表中找到需要被调用的JSModule_1,然后通过JSCore来传递被调用的模块名、方法名以及参数,传递到JS侧,JS在JS模块映射表中找到提前注册好的JSModule_1,然后执行并传入参数。这就是一个完整的原生端向JS端通信的过程。

JS端调用原生代码

????????类似地,看一下JS怎样调用Native方法,首先,JS在初始化时传过来的原生模块映射表中找到需要调用的NativeModule_1,然后通过JSCore传递被调用的模块名方法名以及参数到Native侧,Native拿到方法名之后找到注册好的NativeModule_1,然后执行相应的Native方法并传入参数。

? ? ? ? 那么,有人会想,在Native侧执行完之后,如果有返回值,怎么将返回值传到JS侧呢?当JS侧调用原生代码有返回值的时候,流程会相对复杂一些:

? ? ? ? ?如图,可以清晰的看到,如果有返回值的时候,在JS调用NativeModule_1的时候会生成一个回调ID,并将回调ID一起传递给Native,这个回调ID就是用来标识当Native传递执行结果时,JS拿到执行结果后,将执行结果分发给哪个JS变量。

接下来我们看一下JS是怎么注册到JS模块映射表中的:

const SharedStore = {
  pool: new Map(),
  setItem(key: String, value: any) {
    this.pool.set(key, value);
  },
  getItem(key: string) {
    return this.pool.get(key);
  },
};

const BatchedBridge = globle.__fbBatchedBridge;
BatchedBridge.registerCallableModule("SharedStore", SharedStore);

????????可以看到JS方法是通过调用__fbBatchedBridge上面的registerCallableModule函数来注册的,翻到RN源码的react-native/Libraries/BatchedBridge/BatchedBridge.js处,你会发现,__fbBatchedBridge其实就是一个消息队列,registerCallableModule就是往表里添加一项而已。

class MessageQueue {
  registerCallableModule(name: string, module: Object) {
    this._lazyCallableModules[name] = () => module;
  }
}

const BatchedBridge: MessageQueue = new MessageQueue();
Object.defineProperty(global, '__fbBatchedBridge', {
  configurable: true,
  value: BatchedBridge,
});

module.exports = BatchedBridge;

????????下面看一下Android里是怎样调用JS方法的,可以看到在Android里先定义了JS模块的接口。

import com.facebook.react.bridge.JavaScriptModule
// 定义js模块的接口
public interface SharedStore extends JavaScriptModule {
  void setItem(String key, Object value)
}
// 在ReactNative上下文对象创建后,调用JavaScript模块
ReactInstanceManager reactInstanceManager = getReactNativeHost().getReactInstanceManager();
reactInstanceManager.addReactInstanceEventListener(
  new ReactInstanceManager.ReactInstanceEventListener() {
    @Override
    public void onReactContextInitialied(ReactContext context) {
      context.getCatalystInstance().getJSModule(SharedStore.class).setItem("example", "hello world!")
    }
  }
)

? ? ? ? 在IOS内调用JS十分简单:

// 在ReactNative上下文创建后,直接调用JavaScript模块
RETBridge *bridge = //...
[bridge enqueueJSCall:@"SharedStore" method:@"setItem" args:@[@"example", @"hello world!"] completion:^{
  // 调用JavaScript模块完成的回调函数
}]

?

参考资料

极客时间 - 从原生方向探索React Native

知乎 -?JavaScriptCore 整体介绍

知乎 -?JavaScriptCore全面解析

React Native源码

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章           查看所有文章
加:2021-11-29 16:26:00  更:2021-11-29 16:28:35 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 5:36:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码