Google Play Billing 4构建BillingClient时触发Too many receivers报错
问题根因分析
- Google Play Billing 4.x版本的
BillingClient在调用build()方法构造实例时,内部会自动注册用于接收支付状态变更的广播接收器,每新建一个BillingClient实例就会对应注册一个新的接收器 - 你当前的代码没有限制
BillingManager的实例创建次数,也没有补充BillingClient的销毁逻辑,如果业务侧每次需要调用支付能力时都新建BillingManager实例,就会导致广播接收器持续累积,达到系统单进程1000个广播接收器的上限后就会触发该崩溃 - 仅在部分设备触发的原因是:只有使用时长较长、多次触发支付模块初始化的用户才会累积到1000的阈值,新用户、很少使用支付功能的用户不会触发
修复方案
1. 将BillingManager改为全局单例
全局只初始化一次BillingClient实例,避免重复构造
2. 补充资源销毁逻辑
在应用退出或者不需要使用支付能力时,主动调用BillingClient的endConnection()方法释放内部注册的广播接收器
3. 添加初始化防重逻辑
避免同一时间多次触发初始化操作
修改后参考代码
// 改成单例实现,可根据业务调整为懒汉式 class BillingManager private constructor( private val context: Context, private val firebase: FirebaseManager, private val storage: SharedPreferenceStorage ) { companion object { @Volatile private var INSTANCE: BillingManager? = null // 传入Application Context避免内存泄漏 fun getInstance( context: Context, firebase: FirebaseManager, storage: SharedPreferenceStorage ): BillingManager { return INSTANCE ?: synchronized(this) { INSTANCE ?: BillingManager(context.applicationContext, firebase, storage).also { INSTANCE = it } } } } private var billingClient: BillingClient? = null val initBillingResponse = MutableLiveData<Boolean?>() // 标记是否正在初始化,避免重复触发 private var isInitializing = false private val startConnectionListener = object : BillingClientStateListener { override fun onBillingSetupFinished(result: BillingResult) { isInitializing = false if (result.isResponseOk()) { getSKUs() checkSubscription() initBillingResponse.postValue(true) } else { initBillingResponse.postValue(false) } } override fun onBillingServiceDisconnected() { isInitializing = false initBillingResponse.postValue(false) // 可选:如果需要保持连接可以在这里加重试逻辑,注意限制重试次数避免死循环 } } init { initBilling() } private fun initBilling() { // 已经初始化完成或者正在初始化直接返回 if ((billingClient?.isReady == true) || isInitializing) { initBillingResponse.postValue(true) return } isInitializing = true initBillingResponse.postValue(null) try { billingClient = BillingClient.newBuilder(context) .enablePendingPurchases() .setListener { billingResult, purchases -> if (billingResult.isResponseOk()) { acknowledgePurchases(purchases) checkSubscription() } } .build() billingClient?.startConnection(startConnectionListener) } catch (e: Throwable) { isInitializing = false e.printStackTrace() FirebaseCrashlytics.getInstance().recordException(e) initBillingResponse.postValue(false) } } // 新增销毁方法,在应用退出时主动调用 fun destroy() { runCatching { billingClient?.takeIf { it.isReady }?.endConnection() } billingClient = null INSTANCE = null } // Other functions }
内容的提问来源于stack exchange,提问作者Peyman
相关产品推荐
相关产品推荐

