Home > OS >  Kotlin coroutines: suspended function works on first call but not subsequent calls
Kotlin coroutines: suspended function works on first call but not subsequent calls

Time:09-17

apologies if this is trivial - I am new to Kotlin and coroutines (read Elizarov etc.)

I have an Activity that, in its onCreate(), needs to get a price from the Play Store. I wrote this:

...
lifecycleScope.launch {
    val pd = StoreHelper.getProductDetails(pp!!)
    val price = pd?.oneTimePurchaseOfferDetails?.formattedPrice
    if (price != null) {
        buyNowBtn.text = getString(R.string.buy_price, price)
    } else {
        buyNowBtn.text = getString(R.string.buy_no_price)
    }
}
...

Then, the helper's getProductDetails() I copy-pasted from the docs (notice suspend):

suspend fun getProductDetails(PitchProductId: Long) : ProductDetails? {
    var pd: ProductDetails? = null
    val queryProductDetailsParams =
        QueryProductDetailsParams.newBuilder()
            ...
            .build()

    // leverage queryProductDetails Kotlin extension function
    val productDetailsResult = withContext(Dispatchers.IO) {
        billingClient.queryProductDetails(queryProductDetailsParams)
    }
    if (productDetailsResult.productDetailsList?.isNotEmpty() == true) {
        pd = productDetailsResult.productDetailsList!![0]
    }
    return pd
}

The problem: The Activity can request prices for different products. The first call works. But all subsequent calls don't: I get back a null. I bump the app and get the same thing again (first call works). Looks like I need to cancel something... Sorry, totally lost. Thank you!

CodePudding user response:

This may not solve your issue, but I think you are missing checking whether the query was successful. If you log the failure case, it will lead you toward a solution.

By the way, you don't need to use withContext with queryProductDetails(). I'm pretty sure that's a mistake in the documentation. See here for explanation.

I haven't updated from version 4 to 5, so some of my variable and function names might be wrong in this example. Especially productDetailsResult.result--I'm just guessing at that. Can't find documentation on their extension functions online.

suspend fun getProductDetails(PitchProductId: Long) : ProductDetails? {
    val queryProductDetailsParams =
        QueryProductDetailsParams.newBuilder()
            ...
            .build()
    val productDetailsResult = billingClient.queryProductDetails(queryProductDetailsParams)

    if (productDetailsResult.result.responseCode != BillingResult.OK) {
        val resultList = productDetailsResult.productDetailsList.orEmpty()
        if (resultList.isNotEmpty() {
            return resultList.first()
        }
        Log.e("MyBilling", "Query product details result was OK but list was empty!")
        return null
    }
    Log.e("MyBilling", "Query product details result was: ${productDetailsResult.result.debugMessage}.")
    return null
}

CodePudding user response:

Not an answer, just a working version of Tenfour's comment above ^

// continues from above...
val productDetailsResult =
    billingClient.queryProductDetails(queryProductDetailsParams)

Timber.d("PLAY: queryProductDetails rc=%d", productDetailsResult.billingResult.responseCode)
// https://developer.android.com/reference/com/android/billingclient/api/BillingClient.BillingResponseCode

if (productDetailsResult.billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
    val resultList = productDetailsResult.productDetailsList.orEmpty()
    if (resultList.isNotEmpty()) {
        val pd = resultList.first()
        Timber.d(
            "PLAY: product name is %s at %s",
            pd.name,
            pd.oneTimePurchaseOfferDetails?.formattedPrice ?: "0.00"
        )
        return pd
    }
}
else if ((productDetailsResult.billingResult.responseCode == BillingClient.BillingResponseCode.SERVICE_DISCONNECTED)) {
    Timber.d("PLAY: Service disconnected, reconnecting...")
    connect()
    return getProductDetails(pitchProductId)
}
...
// etc.
  • Related