Why does a suspend function call in a lambda, within a CoroutineScope, produce an error?

I have a retry policy which takes in a lambda, launches a CoroutineScope, increments a retry counter, checks if the maximum retries are reached, calculates a waitTime based on the retry count, delays the scope for this time and finally calls the lambda:

        fun connectionRetryPolicy(block: () -> Unit) {

            Timber.d("connectionRetryPolicy: called")

            // Launch the coroutine to wait for a specific delay
            val scope = CoroutineScope(Job() + Dispatchers.Main)
            scope.launch {

                // Get and increment the current retry counter
                val counter = retryCounter.getAndIncrement()

                // Check if the counter is smaller than the maximum retry count and if so, wait a bit and execute the given function
                if (counter < maxRetry) {

                    // Calculate the time to be waited
                    val waitTime: Long = (2f.pow(counter) * baseDelayMillis).toLong()

                    // Delay the scope for the calculated time
                    delay(waitTime)

                    // Execute the given function
                    block()

                }

                // Else, throw an exception
                else {

                    throw FirebaseNetworkException("Retry count reached")

                }

            }

        }

This method is called to recursively call a suspend function as the lambda, like this:

    private suspend fun connectToGooglePlayBillingService(): BillingResult? {

        Timber.d("connectToGooglePlayBillingService: called")

        return suspendCoroutine { continuation ->

            // If the billingClient is not already ready, start the connection
            if (!playStoreBillingClient.isReady) {

                // Start the connection and wait for its result in the listener
                playStoreBillingClient.startConnection(object: BillingClientStateListener {

                    override fun onBillingServiceDisconnected() {

                        Timber.d("onBillingServiceDisconnected: called")

                        // Retry to connect using the RetryPolicies
                        RetryPolicies.connectionRetryPolicy { connectToGooglePlayBillingService() }

                    }

                    override fun onBillingSetupFinished(billingResult: BillingResult?) {

                        // There is code that does not matter here

                    }

                })

            }

        }

    }

Now, Lint tells me, that connectToGooglePlayBillingService cannot be called inside the lambda because it's a suspend function and it needs to be called inside a CoroutineScope. And as you can see, I do call the lambda inside a CoroutineScope in connectionRetryPolicy.

Is this a bug in Lint or do I do something wrong here? I know, I can create a new CoroutineScope inside the lambda and then call connectToGooglePlayBillingService and I am not sure if this is so wise, regarding performance.

Upvotes: 5

Views: 1532

Answers (1)

apksherlock
apksherlock

Reputation: 8371

Replace

fun connectionRetryPolicy(block: () -> Unit)

with

fun connectionRetryPolicy(block: suspend () -> Unit)

Because your block() will run inside a coroutine, but the other method doesn't know that.

Upvotes: 9

Related Questions