Android订阅更新异常求助:切换失败与Billing v4崩溃问题
Google Play订阅升级/降级问题排查与修复方案
问题1:订阅切换后Play商店显示异常,重复购买出现双订阅
原因分析
- 订阅更新参数有效性不足:代码依赖本地缓存的旧订阅令牌发起更新,若令牌过期或无效,Google Play会直接忽略替换请求,转为创建新订阅;重复操作时未强制走更新流程,直接触发普通购买,导致双订阅共存。
- 未同步Play商店订阅状态:订阅更新完成后,旧订阅会被标记为取消(按设置的 prorate 模式),但代码未主动查询最新状态,本地缓存和商店实际状态不一致。
修复方案
- 使用实时有效令牌发起更新:在构建更新参数前,先查询当前Play商店的有效订阅,确保使用最新的未过期令牌:
// 发起订阅更新前先获取有效旧订阅 billingClient!!.queryPurchasesAsync(BillingClient.SkuType.SUBS) { result, purchases -> if (result.responseCode == BillingClient.BillingResponseCode.OK && purchases.isNotEmpty()) { val validOldPurchase = purchases.firstOrNull { it.purchaseState == Purchase.PurchaseState.PURCHASED } validOldPurchase?.let { val updateParams = BillingFlowParams.SubscriptionUpdateParams.newBuilder() .setOldSkuPurchaseToken(it.purchaseToken) .setReplaceSkusProrationMode(BillingFlowParams.ProrationMode.DEFERRED) .build() // 发起订阅更新流程 billingClient!!.launchBillingFlow(requireActivity(), BillingFlowParams.newBuilder() .setSkuDetails(targetSkuDetails) .setSubscriptionUpdateParams(updateParams) .build() ) } } }
- 强制切换场景走更新流程:当用户已订阅且切换套餐时,禁止触发普通购买逻辑,必须走订阅更新流程。
问题2:订阅更新时setSuccessfulScreen()触发崩溃
原因分析
- UI操作未在主线程执行:
onPurchasesUpdated()回调运行在后台线程,直接操作View违反Android UI线程规则,引发RuntimeException。 - 空指针风险:使用
!!强制解耦View对象,若订阅更新时Fragment/Activity已销毁或重建,View实例为空,触发空指针崩溃(报错信息截断但符合此场景特征)。
修复方案
- 主线程执行UI操作:用
requireActivity().runOnUiThread包裹UI逻辑:
private fun setSuccessfulScreen() { requireActivity().runOnUiThread { group?.visibility = View.GONE group2?.visibility = View.VISIBLE subscribeBtn?.visibility = View.GONE lottieAnimation?.playAnimation() } }
- 增加生命周期判断与安全调用:避免强制非空断言,先判断Fragment状态确保View可用:
private fun setSuccessfulScreen() { if (!isAdded) return requireActivity().runOnUiThread { group?.visibility = View.GONE group2?.visibility = View.VISIBLE subscribeBtn?.visibility = View.GONE lottieAnimation?.let { if (!it.isAnimating) it.playAnimation() } } }
额外优化建议
- 完善错误处理:当前仅处理
BillingResponseCode.OK场景,需补充用户取消购买、网络错误等分支,避免状态不一致。 - 同步订阅状态:App启动或进入订阅页面时,主动调用
queryPurchasesAsync获取最新状态,更新本地缓存。 - 添加日志:在订阅更新关键节点记录日志,便于排查Play商店返回的错误码(如
ITEM_ALREADY_OWNED)。
内容的提问来源于stack exchange,提问作者Binary
相关产品推荐
相关产品推荐

