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

如何解决BillingClient与BillingDataSource(BillingClientWrapper)的循环依赖注入问题?

如何解决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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 12:44:32