React Native Kotlin原生模块对接Google Wallet startActivityForResult报错修复
我正在用Kotlin开发React Native原生模块,实现向Google Wallet添加通用凭证的功能。按照Android端Google Wallet API文档,需要实现onActivityResult函数获取用户操作结果;同时遵循React Native监听onActivityResult的指引,调用walletClient.savePassesJwt(jwtSignedToken, this, addToGoogleWalletRequestCode)方法,但因Intent使用错误,JavaScript端抛出"No Activity found to handle Intent { }"错误,错误栈如下:
{"info": "{ \"nativeStackAndroid\": [ { \"lineNumber\": 2174, \"file\": \"Instrumentation.java\", \"methodName\": \"checkStartActivityResult\", \"class\": \"android.app.Instrumentation\" }, { \"lineNumber\": 1805, \"file\": \"Instrumentation.java\", \"methodName\": \"execStartActivity\", \"class\": \"android.app.Instrumentation\" }, { \"lineNumber\": 5596, \"file\": \"Activity.java\", \"methodName\": \"startActivityForResult\", \"class\": \"android.app.Activity\" }, { \"lineNumber\": 597, \"file\": \"ComponentActivity.java\", \"methodName\": \"startActivityForResult\", \"class\": \"androidx.activity.ComponentActivity\" }, { \"lineNumber\": 5554, \"file\": \"Activity.java\", \"methodName\": \"startActivityForResult\", \"class\": \"android.app.Activity\" }, { \"lineNumber\": 583, \"file\": \"ComponentActivity.java\", \"methodName\": \"startActivityForResult\", \"class\": \"androidx.activity.ComponentActivity\" }, { \"lineNumber\": 112, \"file\": \"GoogleWalletModule.kt\", \"methodName\": \"savePassToGoogleWallet\", \"class\": \"com.peaktowertech.digivcard.GoogleWalletModule\" }, { \"lineNumber\": -2, \"file\": \"Method.java\", \"methodName\": \"invoke\", \"class\": \"java.lang.reflect.Method\" }, { \"lineNumber\": 372, \"file\": \"JavaMethodWrapper.java\", \"methodName\": \"invoke\", \"class\": \"com.facebook.react.bridge.JavaMethodWrapper\" }, { \"lineNumber\": 188, \"file\": \"JavaModuleWrapper.java\", \"methodName\": \"invoke\", \"class\": \"com.facebook.react.bridge.JavaModuleWrapper\" }, { \"lineNumber\": -2, \"file\": \"NativeRunnable.java\", \"methodName\": \"run\", \"class\": \"com.facebook.jni.NativeRunnable\" }, { \"lineNumber\": 942, \"file\": \"Handler.java\", \"methodName\": \"handleCallback\", \"class\": \"android.os.Handler\" }, { \"lineNumber\": 99, \"file\": \"Handler.java\", \"methodName\": \"dispatchMessage\", \"class\": \"android.os.Handler\" }, { \"lineNumber\": 27, \"file\": \"MessageQueueThreadHandler.java\", \"methodName\": \"dispatchMessage\", \"class\": \"com.facebook.react.bridge.queue.MessageQueueThreadHandler\" }, { \"lineNumber\": 226, \"file\": \"Looper.java\", \"methodName\": \"loopOnce\", \"class\": \"android.os.Looper\" }, { \"lineNumber\": 313, \"file\": \"Looper.java\", \"methodName\": \"loop\", \"class\": \"android.os.Looper\" }, { \"lineNumber\": 228, \"file\": \"MessageQueueThreadImpl.java\", \"methodName\": \"run\", \"class\": \"com.facebook.react.bridge.queue.MessageQueueThreadImpl$4\" }, { \"lineNumber\": 1012, \"file\": \"Thread.java\", \"methodName\": \"run\", \"class\": \"java.lang.Thread\" } ], \"userInfo\": null, \"message\": \"No Activity found to handle Intent { }\", \"code\": \"E_FAILED_TO_SHOW_WALLET_FLOW\" }", "message": "Add to google wallet failed"}
我的Kotlin原生模块代码如下:
package com.example.something; import android.app.Activity import android.content.Intent import android.util.Log import com.facebook.react.bridge.BaseActivityEventListener import com.facebook.react.bridge.Callback import com.facebook.react.bridge.LifecycleEventListener import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod import com.google.android.gms.pay.Pay import com.google.android.gms.pay.PayClient import com.google.android.gms.pay.PayApiAvailabilityStatus class GoogleWalletModule(reactContext: ReactApplicationContext, private var walletClient: PayClient=Pay.getClient(reactContext)) : ReactContextBaseJavaModule(reactContext) { private val addToGoogleWalletRequestCode = 1000 private var saveToWalletPromise: Promise? = null private val activityEventListener = object : BaseActivityEventListener() { override fun onActivityResult( activity: Activity?, requestCode: Int, resultCode: Int, data: Intent? ) { if (requestCode == addToGoogleWalletRequestCode) { saveToWalletPromise?.let { promise -> when (resultCode) { Activity.RESULT_OK -> { // Pass saved successfully } Activity.RESULT_CANCELED -> { // Save operation canceled promise.reject(E_ADD_TO_WALLET_CANCELLED, "Add to wallet was cancelled") } PayClient.SavePassesResult.SAVE_ERROR -> data?.let { intentData -> val errorMessage = intentData.getStringExtra(PayClient.EXTRA_API_ERROR_MESSAGE) // Handle error promise.reject(E_FAILED_TO_SAVE_PASS,errorMessage) } else -> { // Handle unexpected (non-API) exception } } } } } } init { reactContext.addActivityEventListener(activityEventListener) } override fun getName() = "GoogleWalletModule" @ReactMethod fun isGoogleWalletApiAvailable(myFailureCallback: Callback, mySuccessCallback: Callback) { walletClient .getPayApiAvailabilityStatus(PayClient.RequestType.SAVE_PASSES) .addOnSuccessListener { status -> if (status == PayApiAvailabilityStatus.AVAILABLE) { // The API is available, show the button in your UI mySuccessCallback.invoke(true) } else { // The user or device is not eligible for using the Pay API mySuccessCallback.invoke(false) } } .addOnFailureListener { // Hide the button and optionally show an error message myFailureCallback.invoke("E_WRONG") } } @ReactMethod fun savePassToGoogleWallet(jwtSignedToken: String,promise: Promise) { val activity = currentActivity if (activity == null) { promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist") return } saveToWalletPromise = promise try { val igniterIntent = Intent().apply { () -> walletClient.savePassesJwt(jwtSignedToken, activity, addToGoogleWalletRequestCode) } activity.startActivityForResult(igniterIntent,addToGoogleWalletRequestCode) } catch (t: Throwable) { saveToWalletPromise?.reject(E_FAILED_TO_SHOW_ADD_TO_WALLET, t) saveToWalletPromise = null } } companion object { const val E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST" const val E_ADD_TO_WALLET_CANCELLED = "E_PICKER_CANCELLED" const val E_FAILED_TO_SHOW_ADD_TO_WALLET = "E_FAILED_TO_SHOW_WALLET_FLOW" const val E_FAILED_TO_SAVE_PASS = "E_FAILED_TO_SAVE_PASS" } }
我不熟悉Android开发,需要解决这个问题。
问题出在savePassToGoogleWallet方法里的Intent使用错误:你手动创建了一个空的Intent,然后尝试启动它,但这个空Intent没有指定任何要启动的Activity,所以系统抛出"No Activity found to handle Intent"错误。实际上,walletClient.savePassesJwt方法本身就会负责启动Google Wallet的Activity并处理startActivityForResult的逻辑,不需要你手动调用activity.startActivityForResult。
修正后的savePassToGoogleWallet方法代码如下:
@ReactMethod fun savePassToGoogleWallet(jwtSignedToken: String,promise: Promise) { val activity = currentActivity if (activity == null) { promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist") return } saveToWalletPromise = promise try { // 直接调用savePassesJwt,它会自动启动对应的Activity并处理请求码 walletClient.savePassesJwt(jwtSignedToken, activity, addToGoogleWalletRequestCode) } catch (t: Throwable) { saveToWalletPromise?.reject(E_FAILED_TO_SHOW_ADD_TO_WALLET, t) saveToWalletPromise = null } }
关键修改点:
- 移除了手动创建空
Intent的代码,不需要构造igniterIntent - 直接调用
walletClient.savePassesJwt,该方法内部已经实现了启动Activity并传递请求码的逻辑,会自动触发onActivityResult回调
另外,补充一个小问题:在onActivityResult的Activity.RESULT_OK分支里,需要调用promise.resolve来通知JavaScript端操作成功,否则Promise会一直处于pending状态,修改如下:
Activity.RESULT_OK -> { // Pass saved successfully promise.resolve("Pass added to Google Wallet successfully") }
内容的提问来源于stack exchange,提问作者7th storm

