调用billingClient.launchBillingFlow前设置的成员变量在回调中丢失
问题诊断与解决方案
核心问题根源
你遇到的问题本质是每次调用purchase()方法时都会创建全新的GooglePlayPay实例:
- 调用
purchase()时,新实例设置了purchaseCallback并启动支付流程,但方法执行完后,这个实例因为没有强引用持有,很快被GC回收 - 支付流程需要用户交互,耗时远长于
getProduct()的API调用,等回调触发时,原来设置了回调的GooglePlayPay实例已经被销毁,导致purchaseCallback变为null
而getProduct()能正常工作,是因为它的异步流程完成速度快,实例还没被回收就触发了回调。
解决方案:复用GooglePlayPay实例
1. 让GooglePlayPayJSInterface持有唯一实例
不再每次调用都创建新的GooglePlayPay对象,而是在JSInterface中保留一个全局实例:
class GooglePlayPayJSInterface(val context: WebAppActivity) { // 持有唯一的GooglePlayPay实例 private val googlePlayPay = GooglePlayPay(context) @JavascriptInterface fun getProduct(str:String) { googlePlayPay.getProduct(str, ::getProductCallback) } fun getProductCallback(details: ProductDetails){ context.postToJs("""{"data":{"productId":"${details.productId}", "name": "${details.name}", "price":"${details.oneTimePurchaseOfferDetails?.formattedPrice?:"n/a"}"}}""") } @JavascriptInterface fun purchase(str:String) { googlePlayPay.purchase(str, ::purchaseCallback) } fun purchaseCallback(success:Boolean){ val result = if(success) """{"data":{"result":"success"}}""" else """{"errcode":"Purchase failed"}""" context.postToJs(result) } // ...其他代码 }
2. 优化GooglePlayPay的生命周期管理
确保BillingClient和Activity生命周期绑定,避免内存泄漏或回调异常:
class GooglePlayPay(val context: NativeAppActivity) { private lateinit var billingClient: BillingClient var purchaseCallback: ((Boolean)->Unit)?=null private val purchasesUpdatedListener = MyPurchasesUpdatedListener() init { // 初始化BillingClient并绑定监听 billingClient = BillingClient.newBuilder(context) .setListener(purchasesUpdatedListener) .enablePendingPurchases() .build() // 连接Google Play计费服务 billingClient.startConnection(object : BillingClientStateListener { override fun onBillingSetupFinished(billingResult: BillingResult) { if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) { // 初始化成功,可正常发起支付 } } override fun onBillingServiceDisconnected() { // 断开连接时自动重试 billingClient.startConnection(this) } }) } inner class MyPurchasesUpdatedListener : PurchasesUpdatedListener { override fun onPurchasesUpdated(billingResult: BillingResult, purchases: List<Purchase>?) { Log.d("fp_purchUpdatedListener","PurchaseCallback: ${purchaseCallback.toString()}") when(billingResult.responseCode) { BillingClient.BillingResponseCode.OK -> { purchases?.forEach { handlePurchase(it) } } BillingClient.BillingResponseCode.USER_CANCELED -> { // 用户取消支付,回调失败 purchaseCallback?.invoke(false) } else -> { // 其他错误场景,回调失败 purchaseCallback?.invoke(false) } } // 回调后清空引用,避免重复触发 purchaseCallback = null } } fun handlePurchase(purchase: Purchase){ // 这里处理订单验证、商品交付逻辑 Log.d("fp_handlePurchase","PurchaseCallback: ${purchaseCallback.toString()}") // 处理完成后回调成功 purchaseCallback?.invoke(true) // 清空回调引用 purchaseCallback = null } fun purchaseProduct(productDetails: ProductDetails, cb:(Boolean)->Unit) { purchaseCallback = cb Log.d("fp_purchaseProduct","2.PurchaseCallback: ${purchaseCallback.toString()}") // 构建支付参数 val billingFlowParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList( listOf( BillingFlowParams.ProductDetailsParams.newBuilder() .setProductDetails(productDetails) .build() ) ) .build() // 确保BillingClient已就绪再启动支付流程 if (billingClient.isReady) { billingClient.launchBillingFlow(context, billingFlowParams) } else { // 未连接成功时直接回调失败 cb.invoke(false) purchaseCallback = null } } // 配合Activity生命周期销毁资源 fun onDestroy() { if (::billingClient.isInitialized) { billingClient.endConnection() } } }
3. 在Activity销毁时清理资源
在WebAppActivity的生命周期方法中,调用GooglePlayPay的销毁方法,避免内存泄漏:
class WebAppActivity : AppCompatActivity() { private lateinit var jsInterface: GooglePlayPayJSInterface override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // ...初始化WebView等逻辑 jsInterface = GooglePlayPayJSInterface(this) webView.addJavascriptInterface(jsInterface, "GooglePlayPay") } override fun onDestroy() { jsInterface.googlePlayPay.onDestroy() super.onDestroy() } }
内容的提问来源于stack exchange,提问作者fpierrat
相关产品推荐
相关产品推荐

