如何在React Native的@ReactMethod中运行Kotlin的suspend挂起函数
解决方案
核心思路
@ReactMethod作为React Native桥接原生层的注解,不支持suspend修饰,你需要在方法内部通过与原生模块生命周期绑定的协程作用域来启动挂起函数,同时注意异步逻辑的资源管理和结果回传。
实现步骤
- 为你的原生模块定义协程作用域,避免使用
GlobalScope造成内存泄漏,在模块销毁时主动取消作用域 - 在
@ReactMethod中通过作用域的launch方法启动挂起逻辑,将gRPC请求、异常处理、资源释放都放在协程块内部 - 如果需要将gRPC请求结果回传给JS端,可通过React Native提供的
Promise参数进行异步回调
修正后的代码示例
import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import kotlinx.coroutines.launch // 你的原生模块类 class GrpcModule(reactContext: ReactContext) : ReactContextBaseJavaModule(reactContext) { // 定义和模块生命周期绑定的协程作用域 private val moduleScope = CoroutineScope(SupervisorJob() + Dispatchers.IO) override fun getName(): String = "GrpcModule" @ReactMethod fun connect(ipAddress: String, port: Int, promise: Promise) { var channel: ManagedChannel? = null moduleScope.launch { try { channel = ManagedChannelBuilder.forAddress(ipAddress, port).usePlaintext().build() val metadata = Metadata() val guidKey = Metadata.Key.of("GUID", Metadata.ASCII_STRING_MARSHALLER) metadata.put(guidKey, GUID) val stub = DBServiceGrpcKt.DBServiceCoroutineStub(channel!!) val request = GrpcDBService.SignInRequest.newBuilder() .setUserName("user") .setPassword("11111") .build() // 直接调用挂起函数执行gRPC请求 val response = stub.trySignIn(request, metadata) // 请求成功,将结果回传给JS端 promise.resolve(response.toString()) } catch (e: Exception) { Log.d("grpcConnect", e.localizedMessage ?: "未知错误") // 请求失败,回传异常给JS端 promise.reject("GRPC_ERROR", e.localizedMessage, e) } finally { // 异步逻辑完成后再关闭channel channel?.shutdown() } } } // 原生模块销毁时取消所有未完成的协程,避免内存泄漏 override fun onCatalystInstanceDestroy() { super.onCatalystInstanceDestroy() moduleScope.cancel() } }
注意事项
- 不要在
@ReactMethod外部的finally块中关闭channel,因为协程是异步执行的,外部方法执行完毕时gRPC请求还未完成,直接关闭channel会导致请求失败 - 所有协程都使用自定义的
moduleScope启动,在模块销毁时统一取消,避免后台协程持有上下文造成内存泄漏 - 协程内部的异常必须捕获,未捕获的协程异常会直接导致应用崩溃
内容的提问来源于stack exchange,提问作者Timur Ruziev
相关产品推荐
相关产品推荐

