Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase Event Listener inside Android Worker

I am trying to implement a Worker from Android WorkManager with an Event listener for Firebase database reference. It works fine if the app is in foreground/background. But once I close the app and the worker runs the event listener is not triggering, As far as I can tell I am not getting any error messages related to this in log.

Here is a sample code:

class FirebaseWorker(context: Context, params: WorkerParameters) : Worker(context, params) {
override fun doWork(): Result {
    val firebaseDatabaseRef = FirebaseDatabase.getInstance().reference
    firebaseDatabaseRef.addListenerForSingleValueEvent(object : ValueEventListener {
        override fun onDataChange(dataSnapshot: DataSnapshot) {
            // Handle DataSnapshot
        }

        override fun onCancelled(databaseError: DatabaseError) {
            // Handle DatabaseError
        }
    })

    return Result.SUCCESS
  }
}

Please let me know if there is anything I can do to get and handle Firebase realtime database data in background when app is closed.

like image 659
Sasi Kanth Avatar asked Dec 18 '22 20:12

Sasi Kanth


2 Answers

You need to block execution of doWork() until all the work is complete. Right now, since addListenerForSingleValueEvent is asynchronous, your function is returning SUCCESS immediately, which means that WorkManager assumes that everything is done and allows your app process to stop.

One way to get your function to block is to use a CountDownLatch to force your code to wait until the listener is complete:

override fun doWork(): Result {
    val latch = CountDownLatch(1)
    val firebaseDatabaseRef = FirebaseDatabase.getInstance().reference
    firebaseDatabaseRef.addListenerForSingleValueEvent(object : ValueEventListener {
        override fun onDataChange(dataSnapshot: DataSnapshot) {
            // Handle DataSnapshot
            latch.countDown()
        }

        override fun onCancelled(databaseError: DatabaseError) {
            // Handle DatabaseError
            latch.countDown()
        }
    })

    latch.await()
    return Result.SUCCESS
}

You may also want to consider when to return a different result code in order to tell WorkManager if your work completed should retried or not.

like image 124
Doug Stevenson Avatar answered Dec 20 '22 11:12

Doug Stevenson


As I see, you are using addListenerForSingleValueEvent(), which means that the listener will read the data precisely once. That means that your onDataChange() method gets triggered with the current value (from the cache if available, otherwise from Firebase servers), and stop listening immediately after that. In this case there is no need to remove the listener. The only time addListenerForSingleValueEvent needs to be canceled is if there is no network connection when you attach it and the client doesn't have a local copy of the data, either because there was another active listener or because it has a copy of the data on disk.

If you want to keep listening for changes, you should use addValueEventListener(). Using this kind of listener means that your onDataChange() method gets called immediately with the current data, but (unlike addListenerForSingleValueEvent) the listener will stay active after that and your onDataChange() will be called for later changes too.

You can use addValueEventListener() after an Android application is closed, by not removing it. Normally, once you are using a listener, you also need to remove it according to the life-cycle of the activity. If do not remove the listener, the app will work only for a shot period of time because Android will stop your service if the app is not in the foreground. It does this to save resources when the app isn't being used. It also might stop your app from doing any networking, or even kill the app process completely. There's nothing you can do to prevent this, other than making it a foreground service, as you already mentioned.

A foreground service is probably not the best thing to do for your case, nor is it the best thing for your users. Read more about limitations on background services.

My recommendation is to use Firebase Cloud Messaging to notify your app when something has changed that it might be interested in. So your users will recieve notification even if they will keep their app closed.

like image 41
Alex Mamo Avatar answered Dec 20 '22 09:12

Alex Mamo