打破边界(一):实战编写 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 了!

相关推荐
千里马学框架3 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone3 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui