打破边界(一):实战编写 Android/iOS 原生模块 (Native Modules)

作为 Android 开发者,我们经常需要调用底层的 API(如蓝牙、传感器、特定的 SDK 等)。在 React Native (RN) 中,当纯 JS 无法满足需求时,我们就需要编写 原生模块 (Native Modules) 。

本篇文章将带你了解如何在 RN 中编写原生模块,并与你熟悉的 Android/Kotlin 开发概念进行对比。

1. 原生模块的基本概念

在 RN 旧架构中,JS 与原生层的通信通过 Bridge(桥接) 进行,消息被序列化为 JSON 异步传递。这类似于 Android 中的 Handler 跨线程通信或者 IPC(进程间通信)。

在新架构(Turbo Native Modules / JSI)中,JS 可以直接持有 C++ 对象的引用并同步调用,类似于 JNI 的直接调用,性能大幅提升。不过,这里我们先从最经典的写法入手。

2. 编写 Android 原生模块

在 Android 侧,编写一个原生模块需要继承 ReactContextBaseJavaModule。

Kotlin vs JS 通信

在 Kotlin 中,我们这样定义一个模块:

kotlin 复制代码
package com.myapp
​
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.Promise
​
class MyCustomModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
​
    // 必须重写 getName,这是 JS 端调用该模块的名称
    override fun getName(): String {
        return "MyCustomModule"
    }
​
    // 使用 @ReactMethod 暴露给 JS 的方法
    @ReactMethod
    fun doSomething(message: String, promise: Promise) {
        try {
            val result = "Android received: $message"
            // 相当于 Kotlin Coroutines 的 resume 或是 callback 的 onSuccess
            promise.resolve(result) 
        } catch (e: Exception) {
            promise.reject("Error", e)
        }
    }
}

概念对比分析:

  1. ReactContextBaseJavaModule :相当于 Android 里的一个单例或 Service 类,它的生命周期绑定在 ReactContext 上。
  2. @ReactMethod :类似于 Android 中 WebView 的 @JavascriptInterface。它告诉 RN 框架,这个方法需要暴露给 JS 端。
  3. Promise :在 JS 端使用的是 Promise。在 Android 侧传入 com.facebook.react.bridge.Promise,它的 resolve() 和 reject() 就等价于我们在 Kotlin 协程中使用的 Continuation.resume,或者常见的网络请求 Callback.onSuccess/onError。

3. 注册模块

写完 Module 后,需要将其注册到 ReactPackage 中:

kotlin 复制代码
class MyCustomPackage : ReactPackage {
    override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
        return listOf(MyCustomModule(reactContext))
    }
​
    override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
        return emptyList()
    }
}

这类似于在 AndroidManifest.xml 中注册 Service 或者在 Dagger/Hilt 中提供 Module。最后将 MyCustomPackage 添加到 MainApplication.kt 的 getPackages() 列表中。

4. 在 JS 端调用

javascript 复制代码
import { NativeModules } from 'react-native';
​
const { MyCustomModule } = NativeModules;
​
async function testModule() {
  try {
    const response = await MyCustomModule.doSomething("Hello from JS!");
    console.log(response); // 输出: Android received: Hello from JS!
  } catch (error) {
    console.error(error);
  }
}

5. 总结与进阶

  • 同步 vs 异步 :默认情况下 @ReactMethod 是异步的(运行在 Native Modules 线程,类似 Dispatchers.IO)。如果你需要同步方法(阻塞 JS 线程),可以使用 @ReactMethod(isBlockingSynchronousMethod = true),但要慎用,避免卡顿主线程。
  • 事件发送 :如果 Android 需要主动给 JS 发消息(比如传感器数据流),可以使用 DeviceEventManagerModule.RCTDeviceEventEmitter。这就类似于 Android 中的 EventBus 或 Kotlin 的 SharedFlow/BroadcastChannel。

掌握了原生模块的编写,你就可以在 RN 中随心所欲地调用任何 Android 原生 SDK 了!

相关推荐
好的佩奇19 分钟前
Dart 之方法、操作符与可调用对象
android·flutter·dart
恋猫de小郭36 分钟前
Flutter Impeller Vulkan 内存持续上涨,问题没想到是文字阴影
android·前端·flutter
张小姐的猫11 小时前
【MySQL】访问 & 数据流动原理
android·数据库·mysql·adb·1024程序员节
朱涛的自习室14 小时前
212 个需求、22 万行代码:Agent 接管开源项目的 19 天
android·前端·人工智能
多客:duokelijie16 小时前
代练订单系统选型思路:跨品类游戏接入、订单分层管理、自动结算逻辑拆解!
数据库·mysql·微信小程序·前端框架·uni-app·php·持续部署
传奇开心果19 小时前
【Flutter踩坑排雷】第3课:const 误用坑|const 错误使用引发 UI 异常与性能损耗
android·flutter
哲鲤19 小时前
AOSP思绎|Android 多媒体:一段视频为什么需要这么多服务
android
阿杰m19 小时前
Android源码第三章:Handler、Looper 与 MessageQueue——一条消息如何入队、唤醒并执行
android
海雅达手持终端PDA1 天前
巡线、深井、爆管抢修与极寒水网的硬件矩阵怎么配?海雅达水务巡检终端PDA选型指南
android·物联网·5g·集成测试·能源·政务
恋猫de小郭1 天前
Flutter 开始支持 Jetpack Compose Platform View
android·前端·flutter