firebase-android-sdk
firebase-android-sdk copied to clipboard
Bug/Feature Request: Update FirebaseMessagingService to extend from LifecycleService
[READ] Step 1: Are you in the right place?
Issues filed here should be about bugs in the code in this repository. If you have a general question, need help debugging, or fall into some other category use one of these other channels:
- For general technical questions, post a question on StackOverflow with the firebase tag.
- For general Firebase discussion, use the firebase-talk google group.
- For help troubleshooting your application that does not fall under one of the above categories, reach out to the personalized Firebase support channel.
[REQUIRED] Step 2: Describe your environment
Firebase android sdk
[REQUIRED] Step 3: Describe the problem
Currently the FirebaseMessagingService needs to make a call to a server to update when a token is refreshed. This is typically done in the modern way using kotlin + coroutines, but it's tough to launch a suspend function without a lifecycle aware scope. LifecycleService is the solution for this. Can we update FMS to extend from this instead?
See: https://stackoverflow.com/questions/68156680/how-to-start-a-background-thread-with-coroutine-on-a-service
I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.
Hey @ColtonIdle Thanks for bringing this up. Our engineers will have a look and discuss this further once they have the time.
For the time being, if you'd like to make your FirebaseMessagingService
lifecycle-aware, you can actually implement a LifecycleOwner
and implement a few methods:
class MyFirebaseMessagingService : FirebaseMessagingService(), LifecycleOwner {
private val mDispatcher = ServiceLifecycleDispatcher(this)
override fun onCreate() {
mDispatcher.onServicePreSuperOnCreate()
super.onCreate()
}
override fun onStart(intent: Intent?, startId: Int) {
mDispatcher.onServicePreSuperOnStart()
super.onStart(intent, startId)
}
override fun onDestroy() {
mDispatcher.onServicePreSuperOnDestroy()
super.onDestroy()
}
override fun getLifecycle(): Lifecycle = mDispatcher.lifecycle
override fun onNewToken(token: String) {
lifecycleScope.launch {
sendRegistrationToServer(token)
}
}
}
I believe it can work as a temporary workaround. See the full list of changes here: https://github.com/firebase/quickstart-android/commit/9ed12fd91eb0fdafc9477627cce881041eb73388
Another way you can launch a coroutine from FirebaseMessagingService
is by declaring its scope and cancelling it on the Service's onDestroy()
:
class MyFirebaseMessagingService : FirebaseMessagingService() {
private val job = SupervisorJob()
private val coroutineScope = CoroutineScope(Dispatchers.Default + job)
override fun onDestroy() {
coroutineScope.cancel()
super.onDestroy()
}
override fun onNewToken(token: String) {
coroutineScope.launch {
sendRegistrationToServer(token)
}
}
}
I feel like this is much less verbose than having to deal with lifecycle events, if your main goal is to launch a coroutine.
Thanks, the temporary workarounds are appreciated. Looking forward to when cororutines can be launched natively inside of FirebaseMessagingService.
Hello sir
I need your help Space between lines of poems data in firebase database using \n is not working! I want to display poems like this in my app from firebase database. This is my home This is my home This is my home This is my home Using \n not working Can you help me? [email protected] Thanks
Hi @Mohammadbasharsahak, could you open a new issue so we can help you with your concern? Thanks!
What to do if onDestroy calls before coroutine finishes it's job
I wonder is this solution https://github.com/firebase/firebase-android-sdk/issues/3851#issuecomment-1172482071 still the best one we've got at this point @thatfiredev ? Or has there been any new APIs to give us a coroutine just like WorkManager does lately?
And if this is our best bet still, I wonder, is the Dispatchers.Default
necessary when creating this CoroutineScope? What will it use if we do not provide any explicit dispatcher?
I wonder, since the functions are already annotated with @WorkerThread, don't we get guarantees that all of it is running in a background thread anyway, so we can somehow avoid having to deal with creating the coroutine scope ourselves and do something else instead?
@thatfiredev
I am creating a coroutine scope and cancelling it in onDestroy(). Is there any possibility of onDestroy() being called before the scope completes its job?
Is the onDestroy() called immediately after onMessageReceived() finishes?
@Khudoyshukur
Yes, FirebaseMessagingService
's onDestroy()
can be called immediately after it has returned from onMessageReceived()
in certain cases, so it probably shouldn't be relied upon to continue work after the message has been handled.
@gsakakihara Hello!
Thank you for answering in this issue, it's very useful.
I have some another questions about using coroutines in FCM. If I understand you correctly, I cannot execute some async things inside of onMessageReceived
, right?
For example, if I'm doing this way, my coroutine can be killed before it was finished?
override fun onMessageReceived(remoteMessage: RemoteMessage) {
Log.d(TAG, "Got push message: ${remoteMessage.rawData.toString()}")
coroutineScope.launch {
val res = tryDoSomethingAsync()
res.collect() { r -> Log.d(TAG, "Finished coroutine")}
}
}
So, is there any way to run some long-running async thing after receiving notification inside of onMessageReceived
?
@rostik404 I was also facing the same issue. Now, I am enqueueing a worker class for long-running async work in onMessageReceived()
@Khudoyshukur, sounds good, thank you
Are you creating instance of WorkManager inside of onMessageReceived? Could you please provide some example?
@rostik404
Yes, inside onMessageReceived(). I provide an example:
override fun onMessageReceived(remoteMessage: RemoteMessage) {
...
val workRequest = OneTimeWorkRequestBuilder<YourWorker>().build()
WorkManager.getInstance(context).enqueue(workRequest)
}
Pretty much what @Khudoyshukur said. Due to the way FCM message handling works on Android, your app is subject to being killed after you return from onMessageReceived
and you should finish onMessageReceived
within 20 seconds (your app is subject to being killed after that anyway). Anything that could take longer than that should be handled by something like WorkManager.
I can't believe this is still not solved... :(
On top of that, the onStart
method thatfiredev suggested overriding is deprecated, too, and its replacement onStartCommand
is final in EnhancedIntentService
.