如何解决BillingClient与BillingDataSource(BillingClientWrapper)的循环依赖注入问题?
这个循环依赖的坑我之前做Google Billing集成的时候也踩过,确实头疼——BillingClient需要listener才能构建,而listener又得依赖BillingDataSource的内部状态,直接注入肯定绕不开循环。不过不用把listener移出去,咱们可以用代理模式或者Dagger的Lazy来完美解决,下面给你具体的实现思路:
方案一:代理Listener(最稳妥的方式)
核心思路是先创建一个「代理listener」,让BillingClient用这个代理来构建,等BillingDataSource初始化完成后,再把真正的listener绑定到代理上。这样既打破了循环,又能让listener正常访问DataSource的内部成员。
1. 实现代理Listener类
class PurchasesUpdatedListenerProxy : PurchasesUpdatedListener { // 持有真实listener的引用,初始化时为null private var delegate: PurchasesUpdatedListener? = null // 用于绑定真实listener的方法 fun setDelegate(delegate: PurchasesUpdatedListener) { this.delegate = delegate } override fun onPurchasesUpdated(result: BillingResult, purchases: MutableList<Purchase>?) { // 转发事件给真实的listener delegate?.onPurchasesUpdated(result, purchases) } }
2. 修改依赖注入配置
先把代理类注册为单例:
@Provides @Singleton fun providePurchasesListenerProxy(): PurchasesUpdatedListenerProxy { return PurchasesUpdatedListenerProxy() }
然后修改provideBillingClient,用代理listener来构建客户端:
@Provides @Singleton fun provideBillingClient( @ApplicationContext context: Context, listenerProxy: PurchasesUpdatedListenerProxy ): BillingClient { return BillingClient.newBuilder(context) .setListener(listenerProxy) // 这里传代理 .enablePendingPurchases() .build() }
3. 调整BillingDataSource的初始化
让DataSource依赖代理类,在初始化时把自己的真实listener绑定到代理:
class BillingDataSource( private val client: BillingClient, listenerProxy: PurchasesUpdatedListenerProxy ) { private val _purchases = MutableStateFlow<List<Purchase>?>(mutableListOf()) val purchases = _purchases.asStateFlow() // 把listener改成私有的,内部使用 private val purchasesUpdatedListener = PurchasesUpdatedListener { result, purchases -> _purchases.value = purchases } // 初始化时绑定代理 init { listenerProxy.setDelegate(purchasesUpdatedListener) } }
最后更新DataSource的注入方法:
@Provides @Singleton fun provideBillingDataSource( client: BillingClient, listenerProxy: PurchasesUpdatedListenerProxy ): BillingDataSource = BillingDataSource(client, listenerProxy)
这样整个依赖链就变成了:BillingClient依赖Proxy → BillingDataSource依赖BillingClient和Proxy → Proxy不依赖任何一方,完美打破循环,而且listener依然能正常访问_purchases。
方案二:用Dagger的Lazy延迟初始化
如果不想额外写代理类,也可以用Dagger提供的Lazy来延迟获取BillingDataSource的实例,避免循环初始化。
修改注入配置
@Provides @Singleton fun provideBillingClient( @ApplicationContext context: Context, dataSourceLazy: Lazy<BillingDataSource> // 用Lazy包裹 ): BillingClient { // 创建一个临时listener,当事件触发时再从Lazy中获取真实的listener val proxyListener = PurchasesUpdatedListener { result, purchases -> dataSourceLazy.get().PURCHASES_UPDATED_LISTENER.onPurchasesUpdated(result, purchases) } return BillingClient.newBuilder(context) .setListener(proxyListener) .enablePendingPurchases() .build() }
同时要保证BillingDataSource里的PURCHASES_UPDATED_LISTENER是public的,这样临时listener才能调用它。
这种方式更简洁,但要注意:第一次触发onPurchasesUpdated时才会真正初始化BillingDataSource(如果之前没有其他地方注入过它),所以要确保在业务逻辑中,DataSource会在购买事件触发前被初始化,避免潜在的问题。
备注:内容来源于stack exchange,提问作者Zero

