JavaScript内核与高级编程之:`JavaScript` 的 `React Native`:其在原生移动应用中的 `Bridge` 通信机制。

早上好,各位!今天咱们来聊聊 React Native 的“神秘桥梁”—— Bridge 通信机制。 别害怕,听起来高大上,其实就是个“翻译官”,让 JavaScript 代码能指挥原生应用干活。

一、React Native 里的“语言不通”问题

想象一下,你跟一个只会说中文的老大爷(原生代码)想让他帮你买包烟,但是你只会说英文(JavaScript 代码)。怎么办? 找个翻译(Bridge)!

React Native 也是一样。 JavaScript 代码运行在 JavaScript 引擎里,而原生代码(比如 Objective-C/Swift for iOS, Java/Kotlin for Android)运行在各自的平台上。 它们就像两个说着不同语言的老大爷,直接对话是不可能的。

二、Bridge:架起沟通的桥梁

Bridge 的作用就是在这两种语言之间架起一座桥梁,负责翻译和传递信息。它让 JavaScript 代码可以调用原生模块,实现各种原生功能,比如访问摄像头、读取定位等等。

三、Bridge 的工作原理:异步消息队列

Bridge 的核心是“异步消息队列”。 啥意思呢? 就像一个排队系统, JavaScript 代码发出的指令(消息)会被放到队列里,然后 Bridge 会按照顺序把这些指令“翻译”成原生代码能理解的指令,再交给原生模块去执行。 执行完后,原生模块会把结果也放到队列里,Bridge 再把结果“翻译”成 JavaScript 代码能理解的形式,返回给 JavaScript 代码。

整个过程是异步的,也就是说, JavaScript 代码发出指令后,不用等着原生模块执行完,可以继续做其他事情。 等原生模块执行完,结果会通过回调函数或者 Promise 的方式通知 JavaScript 代码。

四、Bridge 通信的详细流程

我们可以把 Bridge 的通信流程分解成以下几个步骤:

  1. JavaScript 调用原生模块:

    • JavaScript 代码通过特定的 API(比如 NativeModules )调用原生模块的方法。
    • 这个调用会被序列化成一个 JSON 对象,包含模块名、方法名、参数等信息。
    • 这个 JSON 对象会被发送到 Bridge 的 JavaScript 端。
  2. Bridge 处理 JavaScript 端的消息:

    • Bridge 的 JavaScript 端接收到 JSON 对象后,会把它放到消息队列里。
    • Bridge 的原生端会不断地从消息队列里取出消息。
  3. Bridge 翻译消息:

    • Bridge 的原生端会根据 JSON 对象里的信息,找到对应的原生模块和方法。
    • 它会把 JavaScript 的数据类型转换成原生数据类型,比如把 JavaScript 的字符串转换成 Java 的 String 或者 Objective-C 的 NSString
    • 然后,它会调用原生模块的方法,并把转换后的参数传递给它。
  4. 原生模块执行任务:

    • 原生模块执行完任务后,会把结果返回给 Bridge 的原生端。
    • 结果也会被转换成 JavaScript 可以理解的数据类型。
  5. Bridge 返回结果:

    • Bridge 的原生端把结果发送到 Bridge 的 JavaScript 端。
    • Bridge 的 JavaScript 端会根据之前调用的方式(回调函数或者 Promise ),把结果返回给 JavaScript 代码。

五、代码示例:用 Bridge 调用原生模块

咱们来看一个简单的例子,用 React Native 调用原生模块,显示一个 Toast 提示框。

1. 原生模块(Android – Java):

package com.example.toastmodule;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import android.widget.Toast;

public class ToastModule extends ReactContextBaseJavaModule {

    private static ReactApplicationContext reactContext;

    public ToastModule(ReactApplicationContext context) {
        super(context);
        reactContext = context;
    }

    @Override
    public String getName() {
        return "ToastModule";  // JavaScript 里要用到这个名字
    }

    @ReactMethod
    public void show(String message, int duration) {
        Toast.makeText(reactContext, message, duration).show();
    }
}

2. 原生模块(iOS – Objective-C):

#import "ToastModule.h"
#import <UIKit/UIKit.h>

@implementation ToastModule

RCT_EXPORT_MODULE();  // JavaScript 里要用到这个名字

RCT_EXPORT_METHOD(show:(NSString *)message duration:(NSInteger)duration)
{
  dispatch_async(dispatch_get_main_queue(), ^{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:nil
                                                                             message:message
                                                                      preferredStyle:UIAlertControllerStyleAlert];
    [self presentAlert:alertController duration:duration];
  });
}

- (void)presentAlert:(UIAlertController *)alertController duration:(NSInteger)duration {
    [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alertController animated:YES completion:nil];

    int dismissTime = duration == 0 ? 2 : 3.5; //0: short, 1: long
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(dismissTime * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [alertController dismissViewControllerAnimated:YES completion:nil];
    });
}

@end

3. JavaScript 代码:

import { NativeModules, Button, View } from 'react-native';

const { ToastModule } = NativeModules;  // 注意这里的名字要和原生模块里 `getName()` 返回的值一样

function App() {
  const showToast = () => {
    ToastModule.show('Hello from React Native!', ToastModule.SHORT); // 调用原生模块的 `show` 方法
  };

  return (
    <View>
      <Button title="Show Toast" onPress={showToast} />
    </View>
  );
}

export default App;

解释:

  • 原生模块: 定义了一个名为 ToastModule 的模块,它有一个 show 方法,可以显示一个 Toast 提示框。 getName() 方法返回模块的名字,这个名字会在 JavaScript 代码里用到。 @ReactMethod (Java) 和 RCT_EXPORT_METHOD (Objective-C) 标记的方法可以被 JavaScript 调用。
  • JavaScript 代码: 首先从 NativeModules 里拿到 ToastModule 对象。 然后,调用 ToastModule.show 方法,传递消息和持续时间。

注意: 要使用原生模块,需要先在原生项目中注册它们。 具体步骤请参考 React Native 官方文档。

六、Bridge 的优缺点

优点:

  • 可以访问原生功能: 这是 Bridge 最重要的作用。 JavaScript 代码可以通过 Bridge 调用原生模块,访问摄像头、GPS、传感器等等。
  • 提高性能: 对于一些性能敏感的操作,可以放在原生代码里执行,然后通过 Bridge 返回结果给 JavaScript 代码。 这样可以避免 JavaScript 的性能瓶颈。
  • 代码复用: 可以把现有的原生代码封装成 React Native 模块,然后在 JavaScript 代码里调用。 这样可以避免重复造轮子。

缺点:

  • 通信开销: 每次 JavaScript 代码调用原生模块,都需要通过 Bridge 进行序列化、传输、反序列化等操作。 这些操作会带来一定的开销,影响性能。
  • 数据类型转换: JavaScript 和原生代码使用不同的数据类型。 Bridge 需要负责数据类型的转换,这可能会导致一些精度损失或者类型错误。
  • 调试困难: Bridge 通信过程比较复杂,调试起来比较困难。 需要同时调试 JavaScript 代码和原生代码。

七、Bridge 的优化

为了提高 Bridge 的性能,可以采取以下措施:

  • 减少 Bridge 的调用次数: 尽量把多个操作合并成一个 Bridge 调用。 比如,可以一次性传递多个参数,而不是多次调用同一个方法。
  • 使用更高效的数据类型: 尽量使用基本数据类型,比如字符串、数字等。 避免使用复杂的数据结构,比如嵌套的对象和数组。
  • 使用异步操作: 对于一些耗时的操作,可以使用异步操作,避免阻塞 JavaScript 线程。
  • 使用 Fabric: Fabric 是 React Native 的新架构,它使用 JSI (JavaScript Interface) 直接与原生代码通信,避免了 Bridge 的序列化和反序列化开销,可以显著提高性能。

八、Fabric:Bridge 的“升级版”

Fabric 就像 Bridge 的“升级版”,它使用 JSI (JavaScript Interface) 直接与原生代码通信,不再需要序列化和反序列化 JSON 对象。

Fabric 的优点:

  • 性能更高: Fabric 可以显著提高性能,尤其是在 UI 渲染方面。
  • 类型安全: JSI 使用 C++ 作为中间层,可以提供更好的类型安全。
  • 更灵活: Fabric 提供了更灵活的 API,可以更好地控制原生组件的渲染过程。

Fabric 的缺点:

  • 学习成本高: Fabric 的架构比较复杂,学习成本比较高。
  • 兼容性问题: Fabric 目前还处于实验阶段,可能存在一些兼容性问题。

九、JSI:JavaScript Interface

JSI (JavaScript Interface) 是 Fabric 的核心。 它允许 JavaScript 代码直接持有原生对象的引用,并直接调用原生对象的方法。 这样就避免了 Bridge 的序列化和反序列化开销。

JSI 的工作原理:

  1. JavaScript 代码获取原生对象的引用: JavaScript 代码可以通过 JSI 获取原生对象的引用。 这个引用是一个 C++ 对象,它封装了原生对象的指针。
  2. JavaScript 代码调用原生对象的方法: JavaScript 代码可以通过这个引用调用原生对象的方法。 JSI 会把这个调用转发给原生对象。
  3. 原生对象执行任务: 原生对象执行完任务后,会把结果返回给 JSI。
  4. JSI 返回结果给 JavaScript 代码: JSI 会把结果转换成 JavaScript 可以理解的数据类型,然后返回给 JavaScript 代码。

十、总结

Bridge 是 React Native 连接 JavaScript 代码和原生代码的桥梁。 它通过异步消息队列的方式传递信息,实现了 JavaScript 代码调用原生模块的功能。 虽然 Bridge 有一些缺点,但是可以通过优化来提高性能。 Fabric 是 Bridge 的“升级版”,它使用 JSI 直接与原生代码通信,可以显著提高性能。

希望今天的讲座能帮助大家更好地理解 React Native 的 Bridge 通信机制。 记住,Bridge 就像一个“翻译官”,让 JavaScript 代码和原生代码可以“愉快地交流”。 而 Fabric 就像一个“直通车”,让它们可以直接对话,效率更高。

下次再见!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注