2

I am using a BroadcastReceiver to send a broadcast to restart the FirebaseMessagingService when the service is destroyed. However, when starting service this way, the FirebaseMessagingService gets immediately destroyed. When the service is destroyed, I will send another broadcast to the receiver and the loop continues indefinitely.

My code for FirebaseMessagingService.

@AndroidEntryPoint
class MessagingService :
    FirebaseMessagingService(),
    FirebaseAuth.AuthStateListener {
    companion object {
        var isRunning = false
        const val REQUEST_CODE = 0
        const val NOTIFICATION_ID = 0
    }

    @Inject
    lateinit var notificationRepository: NotificationRepository
    @Inject
    lateinit var authRepository: AuthRepository

    override fun onCreate() {
        super.onCreate()
        Log.d("MessagingService", "I am created")
        isRunning = true
        FirebaseAuth.getInstance().addAuthStateListener(this)
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d("MessagingService", "I am destroyed")
        isRunning = false
        FirebaseAuth.getInstance().removeAuthStateListener(this)
        val intent = Intent()
        intent.action = "restartMessagingService"
        intent.setClass(this, MessagingServiceRestarter::class.java)
        this.sendBroadcast(intent)
    }

    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        val title = remoteMessage.notification?.title
        val message = remoteMessage.notification?.body
        if (message != null && title != null) {
            sendPushNotification(title, message)
        }
    }

    override fun onNewToken(token: String) {
        saveTokenToSharedPreferences(token)
        authRepository.getUser()?.let {
            val uid = it.uid
            sendTokenToFirestore(uid, token)
        }
    }

    override fun onAuthStateChanged(auth: FirebaseAuth) {
        auth.currentUser?.let {
            val uid = it.uid
            val savedRegistrationToken =
                PreferenceManager.getDefaultSharedPreferences(this)
                    .getString(getString(R.string.fcm_token_shared_pref_key), "")
            savedRegistrationToken?.let { token -> sendTokenToFirestore(uid, token) }
        }
    }

    private fun sendPushNotification(title: String, messageBody: String) {
        val intent = Intent(this, MainActivity::class.java)
        val pendingIntent = PendingIntent.getActivity(this, REQUEST_CODE,
            intent, PendingIntent.FLAG_UPDATE_CURRENT)

        val channelId = getString(R.string.general_notification_channel_id)
        val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
        val notificationBuilder = NotificationCompat.Builder(this, channelId)
            .setSmallIcon(R.drawable.mlearn_logo)
            .setContentTitle(title)
            .setContentText(messageBody)
            .setAutoCancel(true)
            .setSound(defaultSoundUri)
            .setContentIntent(pendingIntent)

        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        val channel = NotificationChannel(
            channelId,
            getString(R.string.general_notification_channel_name),
            NotificationManager.IMPORTANCE_DEFAULT
        )
        notificationManager.createNotificationChannel(channel)
        notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
    }

    private fun saveTokenToSharedPreferences(token: String) {
        val sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
        with (sharedPref.edit()) {
            putString(getString(R.string.fcm_token_shared_pref_key), token)
            apply()
        }
    }

    private fun sendTokenToFirestore(uid: String, token: String) {
        GlobalScope.launch(Dispatchers.IO) {
            notificationRepository.sendNotificationToken(uid, token)
        }
    }
}

My code for the receiver.

class MessagingServiceRestarter : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Log.d("MessagingService", "Restarter" + MessagingService.isRunning.toString())
        if (!MessagingService.isRunning) {
            context.startService(Intent(context, MessagingService::class.java))
            MessagingService.isRunning = true
        }
    }
}

I start my service every time the MainActivity is created.

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d("MessagingService", "MainActivity" + MessagingService.isRunning.toString())
        if (!MessagingService.isRunning) {
            val intent = Intent(this, MessagingService::class.java)
            startService(intent)
            MessagingService.isRunning = true
        }
    }

    override fun onDestroy() {
        val intent = Intent()
        Log.d("MessagingService", "MainActivity" + MessagingService.isRunning.toString())
        intent.action = "restartMessagingService"
        intent.setClass(this, MessagingServiceRestarter::class.java)
        sendBroadcast(intent)
        super.onDestroy()
    }

And I have registered my service and receiver on manifest.

        <service
            android:name=".service.MessagingService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.google.firebase.MESSAGING_EVENT" />
            </intent-filter>
        </service>
        
        <receiver
            android:name=".receiver.MessagingServiceRestarter"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="restartMessagingService"></action>
            </intent-filter>
        </receiver>

How can I keep my FirebaseMessagingService running? I am using this receiver to restart the service when the service is stopped because the app is cleared from the recent apps.

Richard
  • 7,037
  • 2
  • 23
  • 76
  • 1
    Curious about why you want the service to be running? There's no need. Every time your app receives a notification, the service is automatically called. – Basu Jul 19 '21 at 20:05
  • @Basu Because I want to listen for auth changes so I can send the notification token to my Firestore. – Richard Jul 20 '21 at 12:43
  • Why do you need to listen to auth changes? Could you please explain the problem and approach you are using , so that we can understand why you need to have auth listener in Messaging service? – Kamal Nayan Jul 21 '21 at 10:14
  • Also, when you are changing user, say in your login page, just call Firebase to regenerate the token. – Basu Jul 21 '21 at 17:29
  • @KamalNayan As far as I know, tokens from Firebase Messaging are sent by Firebase and we are not really advised to control when the tokens are sent. Right now, I am saving the tokens to my `shared preferences` so that users can use the same device for multiple Google accounts. When users receive tokens for the first time (app install), I won't save the tokens to Firestore. When users log in for the first time (i.e. `on auth change`), I will send the tokens already saved in `shared preferences` to the Firestore. On log out, I will delete the token saved in Firestore so that that user doesn't – Richard Jul 22 '21 at 02:46
  • @KamalNayan ... receive the notification on the device when not logged in. Perhaps I shouldn't include the `on auth trigger` inside my Service and instead save the tokens from `shared preferences` to Firestore when logging in (e.g. in the `ViewModel` code)? – Richard Jul 22 '21 at 02:48
  • @Richard You can try forground service to keep running your services even if you application is killed. – pankaj yadav Jul 22 '21 at 04:12
  • @Richard you can shift the code where you are handling the log out of user (any button using which user logouts from the app) and again when user logs in then use `FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener(this, instanceIdResult -> {SEND TO FIRESTORE}` – Kamal Nayan Jul 22 '21 at 11:30
  • have you tried to add IGNORE_BATTERY_OPTIMIZATION permission to your application? – Ali Jul 22 '21 at 15:38

0 Answers0