1

I am caching my users purchases locally using Room. For this, I created an insert function, which is suspending.

First, in my repository, I called the insert function from a function called launchBillingFlow which is suspending and its signature looks like this:

suspend fun launchBillingFlow(activity, skuDetails)

I have had no errors doing it like this. Afterwards, I created a private function for acknowledging the purchase and moved the insert call to there. It's signature looks like this:

private suspend fun acknowledgePurchase(purchase)

But when I call the insert function from there, I get following error:

Suspension functions can be called only within coroutine body

And I don't understand why this is. I call launchBillingFlow from a coroutine body and it calls acknowledgePurchase. And I am doing a similar thing with querying the SkuDetails, too. Is this a lint bug or am I missing something?

René Jörg Spies
  • 1,544
  • 1
  • 10
  • 29
  • 1
    You sure you’re not calling the other suspend function from a lambda or callback? – Tenfour04 Jul 18 '20 at 00:04
  • @Tenfour04 Good point! I am calling it from the `ResponseListener` for acknowledging purchases which is wrong. I am going to use a `suspendCoroutine` to fix it. Thank you! – René Jörg Spies Jul 18 '20 at 00:11
  • BTW this is not a lint error, it is a hard compile error. It is impossible for Kotlin to compile a call to a suspendable function outside a coroutine. – Marko Topolnik Jul 19 '20 at 06:01

1 Answers1

0

It is not a lint bug. It says you need coroutine context in your method

The lint message is:

suspend function can only be called within coroutine body

I know that suspend function can only be called inside another suspend function and inside coroutine.

In contrast this message says it can only be called inside coroutine but In fact, this is not a contradiction.

To understand the message you can ignore suspend word in it and read the message like this:

The [function name] can only be called within coroutine body because it needs coroutine context.

The reason it needs coroutine context is not because it is suspend it is because inside it you have called a method that needs context.

ygngy
  • 3,630
  • 2
  • 18
  • 29
  • I am aware of this information. The question is, why is this? I can only call `suspend` functions from a coroutine context at some point. And I am chaining other `suspend` functions in my repository on various occasions where it does not bring this error. – René Jörg Spies Jul 17 '20 at 23:00
  • For example, when the user first opens the page showing my offer, I am querying via the `BillingClient` for the `SkuDetails` and when I get these, I am caching them in the same database on another table using basically the same `insert` function. And in this case, `lint` does not complain about it. – René Jörg Spies Jul 17 '20 at 23:04
  • And the `lint` error message also implies that `Suspension functions can be called only within coroutine body` which is generally wrong because suspension functions can be called from other suspend functions, too, as I am doing it on many occasions in my code. – René Jörg Spies Jul 17 '20 at 23:08
  • 1
    This answer is incorrect. You can always call suspend functions from other suspend function. Each suspend function gets its coroutine context from the suspend function that called it. – Tenfour04 Jul 18 '20 at 00:05
  • @Tenfour04 I know suspend function can ONLY be called inside suspend functions or coroutines BUT your comment is incorrect if function A and B both are suspend it does not means you can always call B inside A because B may needs some context or conditions that is not available inside A. the fact that A is suspend is only one condition there may be other conditions. – ygngy Jul 18 '20 at 15:53
  • @Tenfour04 suspend function does NOT have coroutine context on its own the suspend word is only a flag for compiler and you can define a suspend function in a class that do NOT have a coroutine context. – ygngy Jul 18 '20 at 15:55
  • @Bahman That’s a pedantic observation. Of course you can only call a function if you have parameters to pass to it. That’s not what the OP’s error message is talking about. All suspend functions have an implicit context that comes from the suspend function that called it. I didn’t say you need to have an explicit context for a suspend function. You never do except when starting a coroutine with something like `launch`. – Tenfour04 Jul 18 '20 at 17:33