I need to make an api call (suspend function since...
# android
c
I need to make an api call (suspend function since I use retrofit) in my FirebaseMessagingService. What's the proper way to make a suspend call in an Android service? Does this comment still hold true in 2022? https://stackoverflow.com/a/63407811
f
If you're calling from a suspending function you can call retrofit directly. Otherwise you need a scope, you can create one like in that answer, or you could have your own global scope that's bound to the application lifecycle and inject that where needed. Or you inject a scope factory that let's you create a scope in your firebase class.
💡 1
g
If you do not own this service, you can use this upvoted solution from SO
you could have your own global scope that’s bound to the application lifecycle and inject that where needed
This solution is convinient, but careful, it may cause service to leak if you just use some global scope and do not cancel all your jobs on service destroy (because job which you running lifecycle may be longer than potential lifecycle of the service and will not be cancelled automatically)|
💡 1
inject a scope factory that let’s you create a scope in your firebase class.
Yes, it one of ways, but you should inject Factory and cancel created scope onDestroy, otherwise it will have the same issue as global scope (so in reality it’s pertty much the same as SO answer)
💡 1
c
I do "own" the service, but I need to extend from FirebaseMessagingService, but I should be able to add an interface for LifecycleService. I'll do more research into this. Thanks @gildor
Looks like I can't do anything here except for following the advice in the stackoverflow post since I am not in control of FirebaseMessagingService Thanks all
g
do “own” the service, but I need to extend from FirebaseMessagingService
So you do not own it, you extending it 😄
but I should be able to add an interface for LifecycleService.
LifecycleService is not an interface, it’s a base class same way as ComponentActivity. So you probably just should follow SO
c
thanks yeah. i misinterpreted it the first time i read your answer. I filed a feature request with firebase to extend from this LifecycleService instead. thank youu
👍 2
g
Funny, that they already use
EnhancedIntentService
as base class, which does exactly the same, creates executor with lifecycle of the service (shutting down it onDestroy)
i
LifecycleService
is just a shim wrapper around the actual logic in
ServiceLifecycleDispatcher
, which is there exactly if you already have a base class for your service: https://developer.android.com/reference/androidx/lifecycle/ServiceLifecycleDispatcher
g
ah it’s nice, indeed. A bit more work though if one needs only coroutineScope, not all lifecycle events
i
Yeah, you really only need a
val coroutineContext
set up with something like
SupervisorJob() + Dispatchers.Main
and
coroutineScope.cancel()
in
onDestroy()
, everything else is optional, tbh
💡 1
c
I eneded up going with this if anyone is interested
Copy code
@AndroidEntryPoint
class MyFirebaseMessagingService : FirebaseMessagingService() {
  @Inject lateinit var apiService: MyApiService

  private val job = SupervisorJob()
  private val scope = CoroutineScope(<http://Dispatchers.IO|Dispatchers.IO> + job)

//run some suspend functions when needed

  override fun onDestroy() {
    job.cancel()
    super.onDestroy()
  }
g
I would remove job property and just cancel scope itself I also not a fan of using IO by default in the service, it changes default thread of Service (which is run on Main thread by default) and instead wrap to appropriate dispatcher when it’s needed
c
Good points. Since retrofit handles switching of threads, I don't think I need to declare a dispatcher at all. Updated to:
Copy code
@AndroidEntryPoint
class MyFirebaseMessagingService : FirebaseMessagingService() {
  @Inject lateinit var apiService: MyApiService

  private val scope = CoroutineScope(Dispatchers.Main)

//run some suspend functions when needed

  override fun onDestroy() {
    scope.cancel()
    super.onDestroy()
  }
g
You still need Job, othewise it will produce new job for every cororoutine, so no sctructured concurrency
Copy code
private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())
Also it’s fine to specify dispatcher, and Main looks as appropriate choice for Service (Activity, Fragment)
c
Oh thats what you meant. Okay.
Copy code
@AndroidEntryPoint
class MyFirebaseMessagingService : FirebaseMessagingService() {
  @Inject lateinit var apiService: MyApiService

  private val scope = CoroutineScope(Dispatchers.Main + SupervisorJob())

//run some suspend functions when needed

  override fun onDestroy() {
    scope.cancel()
    super.onDestroy()
  }
I learned so much. Thanks for teaching!
👌 1
Got a response from my feature request if anyone comes across this in the future: https://github.com/firebase/firebase-android-sdk/issues/3851#issuecomment-1172482071
f
so basically back to the SO answer
😅 1
431 Views