I have a situation where my BillingDataSource
aka BillingClientWrapper
needs a singleton BillingClient
object, but creating the BillingClient
object can't be completed without a listener that's in the BillingDataSource
:
class BillingDataSource(private val client: BillingClient) {
private val _purchases = MutableStateFlow<List<Purchase>?>(mutableListOf())
val purchases = _purchases.asStateFlow()
val PURCHASES_UPDATED_LISTENER = PurchasesUpdatedListener { result, purchases ->
_purchases.value = purchases
}
}
And the dependency injection:
@Provides
@Singleton
fun provideBillingClient(
@ApplicationContext context: Context,
wrapper: BillingDataSource
) : BillingClient {
return BillingClient.newBuilder(context)
.setListener(wrapper.PURCHASES_UPDATED_LISTENER)
.enablePendingPurchases()
.build()
}
@Provides
@Singleton
fun provideBillingDataSource(
client: BillingClient
) : BillingDataSource = BillingDataSource(client)
The .setListener
requirement when constructing the BillingClient
is turning out to be a real headache. Of course, I can break the circular dependency by putting the listener outside of the BillingDataSource
, but then I'd lose access to members (like _purchases
) inside of BillingDataSource
, and that's hardly ideal.
How do I solve this?
You can extract purchase listener (and possibly some other related methods and properties) to a separate class or object.
// instead of dependency injection
// you can use object here for PurchaseListener
@Singleton
class PurchaseListener @Inject constructor(){
private val _purchases = MutableStateFlow<List<Purchase>?>(mutableListOf())
val purchases = _purchases.asStateFlow()
val callback = PurchasesUpdatedListener { result, purchases ->
_purchases.value = purchases
}
}
@Singleton
class BillingDataSource @Inject constructor(
private val client: BillingClient,
val purchaseListener: PurchaseListener)
@Provides
@Singleton
fun provideBillingClient(
@ApplicationContext context: Context,
purchaseListener: PurchaseListener
) : BillingClient {
return BillingClient.newBuilder(context)
.setListener(purchaseListener)
.enablePendingPurchases()
.build()
}
@Provides
@Singleton
fun provideBillingDataSource(
client: BillingClient,
purchaseListener: PurchaseListener
) : BillingDataSource = BillingDataSource(client, purchaseListener)