You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native Kotlin原生模块对接Google Wallet startActivityForResult报错修复

问题:React Native Kotlin原生模块调用Google Wallet API出现"No Activity found to handle Intent"错误

我正在用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 17:28:12