导读:本期聚焦于坚哥创作的《React Native调用AI模型怎么做?JSI与Native Modules桥接方式深度对比与实践》,敬请观看详情。在移动端集成AI能力时,React Native开发者常面临一个选择:走传统的Native Modules桥接,还是用更底层的JSI直连C++层?本文从两条技术路线的通信原理讲起,分析旧桥接架构的序列化开销瓶颈,以及JSI如何通过共享内存和同步调用突破性能限制。文中给出完整代码示例,演示如何通过JSI封装TensorFlow Lite推理接口,并对比两种方案在模型加载速度、推理延迟、内存占用上的实际差异。最后总结了选型建议:轻量模型桥接足够,高帧率实时推理场景JSI优势明显,同时提示新架构下Turbo Modules对两者的统一趋势。

把AI模型塞进App已经不算新鲜事了,语音识别、图像分类、实时滤镜、离线翻译,这些能力背后都离不开本地推理。对React Native开发者来说,问题在于JS和原生层之间隔着一座桥,模型推理恰恰是对数据传输最敏感的场景。一次推理可能要传几MB的图像数据,如果每次调用都要经过JSON序列化,性能损耗会非常直观地体现在帧率上。本文围绕JSI和Native Modules两种方式展开,从原理到代码逐层拆解。

React Native调用AI模型怎么做?JSI与Native Modules桥接方式深度对比与实践

一、Native Modules桥接:传统方式的原理与局限

Native Modules是React Native最经典的通信方案。JS线程通过NativeModules对象调用原生方法,消息会经过Bridge(桥接层)序列化成JSON字符串,再投递到Native线程执行,执行完毕后又把结果序列化回JS。这个设计在旧架构下工作得很好,但它的每一次调用都伴随着完整的序列化和反序列化过程。

对于普通业务接口,比如调用原生分享、获取设备信息,这种开销可以忽略。但AI推理不一样:输入张量可能是Shape为[1, 224, 224, 3]的浮点数组,包含超过15万个float元素。如果走JSON序列化,这些数字会先被转成字符串,跨线程后再解析回数字,单次传输的开销可能比推理本身还大。

先看一个典型的Native Modules实现,以Android端加载TensorFlow Lite为例:

public class TFLiteModule extends ReactContextBaseJavaModule {
    private Interpreter interpreter;

    TFLiteModule(ReactApplicationContext context) {
        super(context);
    }

    @Override
    public String getName() {
        return "TFLiteModule";
    }

    @ReactMethod
    public void loadModel(String modelPath, Promise promise) {
        try {
            Interpreter.Options options = new Interpreter.Options();
            options.setNumThreads(4);
            interpreter = new Interpreter(
                new File(modelPath), options);
            promise.resolve(true);
        } catch (Exception e) {
            promise.reject("LOAD_ERROR", e.getMessage());
        }
    }

    @ReactMethod
    public void runInference(ReadableArray input, Promise promise) {
        // ReadableArray需要逐个读取元素,再组装成float数组
        float[][][][] inputData = new float[1][224][224][3];
        // 逐元素拷贝,这一步是性能瓶颈
        for (int i = 0; i < input.size(); i++) {
            // ...逐个填充张量数据
        }
        float[][] output = new float[1][1000];
        interpreter.run(inputData, output);
        WritableArray result = new WritableNativeArray();
        for (float v : output[0]) {
            result.pushDouble(v);
        }
        promise.resolve(result);
    }
}

JS侧的调用代码很简单,但隐藏成本不小:

import { NativeModules } from 'react-native';
const { TFLiteModule } = NativeModules;

async function classify(imagePixels) {
  await TFLiteModule.loadModel('mobilenet_v2.tflite');
  const output = await TFLiteModule.runInference(imagePixels);
  console.log('推理结果长度:', output.length);
}

这套流程的局限可以归纳为三点:一是数据必须复制多次(JS对象转JSON、JSON转Java数组、Java数组转ByteBuffer);二是异步通信,无法在JS线程同步拿到结果,做实时相机滤镜时每帧都要等待Promise回调;三是传输大数组时容易触发Bridge拥塞,导致JS线程卡顿。

二、JSI直连:绕过桥接的同步通信机制

JSI(JavaScript Interface)是React Native在0.58版本引入的C++抽象层,它允许C++代码直接持有JS对象的引用并调用其方法,反过来JS也可以直接调用C++暴露的HostObject。JSI的本质是一套与具体JS引擎无关的接口,无论底层是Hermes还是JSC,都通过同一套API操作JS值。

JSI最关键的能力是HostObject。你可以把它理解为一个C++对象被注入到JS全局作用域中,JS访问它的属性时,会直接触发C++函数调用,全程没有序列化,没有线程切换开销(如果实现得当),也没有异步等待。这意味着传一个TypedArray给C++,C++拿到的是指向同一块内存的指针,数据零拷贝。

下面是一个通过JSI暴露同步推理接口的C++实现骨架:

#include <jsi/jsi.h>
#include <vector>

using namespace facebook;

class TFLiteHostObject : public jsi::HostObject {
public:
    // JS调用 tensor.runInference(inputArray) 时进入这里
    jsi::Value get(jsi::Runtime &rt, const jsi::PropNameID &name) override {
        auto methodName = name.utf8(rt);

        if (methodName == "runInference") {
            return jsi::Function::createFromHostFunction(
                rt, name, 1,
                [this](jsi::Runtime &rt,
                       const jsi::Value &,
                       const jsi::Value *args,
                       size_t count) -> jsi::Value {
                    // 直接从TypedArray读取底层缓冲区,零拷贝
                    auto &arrayBuffer =
                        args[0].asObject(rt).getPropertyAsObject(
                            rt, "buffer");
                    auto arrayData =
                        arrayBuffer.getAsArrayBuffer(rt);
                    const float *input =
                        reinterpret_cast<const float *>(
                            arrayData.data(rt));

                    std::vector<float> output(1000);
                    interpreter->Invoke(
                        input, output.data());

                    // 构造返回的TypedArray,同样零拷贝路径
                    jsi::Function arrayCtor = rt.global()
                        .getPropertyAsFunction(rt, "Float32Array");
                    jsi::Object result =
                        arrayCtor.callAsConstructor(rt, 1000)
                            .asObject(rt);
                    // 写入输出数据...
                    return result;
                });
        }
        return jsi::Value::undefined();
    }

private:
    tflite::Interpreter *interpreter = nullptr;
};

// 安装到全局,一般在RN初始化时调用
void install(jsi::Runtime &rt) {
    auto hostObj = std::make_shared<TFLiteHostObject>();
    rt.global().setProperty(
        rt, "nativeTFLite",
        jsi::Object::createFromHostObject(rt, hostObj));
}

JS侧的使用体验发生了质变——同步调用,直接访问全局对象:

// 无需Promise,无异步等待
const output = nativeTFLite.runInference(inputFloat32Array);
const maxIndex = output.indexOf(Math.max(...output));

需要注意线程问题。JSI的HostObject默认在JS线程被调用,而推理是CPU密集操作,直接在JS线程执行会阻塞UI。工程上的做法是在C++侧维护一个推理线程队列,HostObject内部把数据指针投递到工作线程,如果业务允许同步等待就join,否则可以配合Promise风格封装。此外,在Android上需要通过PackagegetCxxBases或在合适的生命周期钩子里执行install,iOS则在初始化Bridge时注入,这部分脚手架工作可以借助react-native-builder-bob之类的工具简化。

三、方案对比与选型建议

两条路线没有绝对优劣,关键看场景。下面从几个维度做对比:

对比维度Native Modules桥接JSI
通信方式异步消息,JSON序列化同步直调,零拷贝
数据传输成本大数组开销显著,多次复制TypedArray共享底层内存
调用延迟毫秒级,受Bridge负载影响微秒级,稳定可预测
开发门槛低,Java/Objective-C即可高,需要C++与构建系统知识
调试便利性Chrome DevTools直接可用断点调试需切换环境

如果只是调用一次模型做图像分类,或者用Core ML处理单张照片,Native Modules完全够用,开发成本低、维护简单,团队里不需要懂C++的人。但如果是相机实时滤镜、逐帧目标检测、流式语音识别这类高频推理场景,JSI的优势会非常明显——实测中,一张224x224的图像张量经桥接传输大约要额外消耗十几毫秒,而JSI路径几乎可以忽略,帧率差距直接体现在体验上。

还有一点值得留意:React Native新架构下的Turbo Modules本身就是构建在JSI之上的,旧的Native Modules会被逐步兼容替代。也就是说,即使现在选择走桥接方案,随着项目升级到新架构,底层通信机制也会自然向JSI靠拢。提前熟悉JSI的编程模型,对长期维护是有价值的。

落地时的建议很朴素:先用Native Modules快速验证模型效果,确认推理链路可行;再评估调用频率和数据规模,如果性能成为瓶颈,把推理接口这一小部分用JSI重写,其余业务模块保持原样。渐进式迁移比推翻重来务实得多。

React NativeJSINative Modules修改时间:2026-09-03 14:55:25

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/20260903/49631.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。