avatarAlex Mamo

Summary

This article discusses how to read data from Firebase Realtime Database using the get() method.

Abstract

The article explains that Firebase Realtime Database is a NoSQL cloud-hosted real-time database from Google. It provides two options for reading data: using a persistent listener or reading the data only once. The get() method was added to the Firebase Realtime Database Android SDK in version 19.6.0, allowing developers to read data precisely once. The article then discusses three ways to get data from Firebase Realtime Database using the get() method: callback, Android Architecture Component LiveData, and Kotlin Coroutines. The article concludes by stating that these three solutions can help developers get data from Firebase Realtime Database using the get() method.

Bullet points

  • Firebase Realtime Database is a NoSQL cloud-hosted real-time database from Google.
  • There are two options for reading data from Firebase Realtime Database: using a persistent listener or reading the data only once.
  • The get() method was added to the Firebase Realtime Database Android SDK in version 19.6.0, allowing developers to read data precisely once.
  • The article discusses three ways to get data from Firebase Realtime Database using the get() method: callback, Android Architecture Component LiveData, and Kotlin Coroutines.
  • The callback solution involves creating an interface with an abstract method that takes a single argument of type Response.
  • The LiveData solution involves creating a new method that sets the response object into a MutableLiveData<Response> object and returns it as a result of the method.
  • The Kotlin Coroutines solution involves defining a suspend function inside the ProductsRepository class that calls get() on the DatabaseReference object and then simply calls await().
  • The article concludes by stating that these three solutions can help developers get data from Firebase Realtime Database using the get() method.

How to read data from Firebase Realtime Database using get()?

Firebase Realtime Database is a NoSQL cloud-hosted real-time database from Google.

When it comes to reading data from Firebase Realtime Database, there are two options available. The first one is to read the data using a persistent listener, meaning that we’ll always be in sync with the Firebase servers, or we can read the data only once. The first option is very helpful when we need to listen for changes in real-time. We can achieve this using Query’s addValueEventListener() method. This method adds a listener for changes in the data at a particular path in the database. Each time the data changes, the listener will be invoked with an immutable snapshot of the data. However, there are cases in which we need to read the data from the database only once.

Even from the beginning in 2015, it was added in the Firebase Realtime Database Android SDK a method called addListenerForSingleValueEvent(). This method adds a listener for a single change, meaning that we can read the data at a particular path exactly once. This method remains part of the Firebase Realtime Database Android SDK ever since.

On the other hand, Cloud Firestore, the newer massively scalable NoSQL cloud-hosted real-time database from Google, has another type of mechanism for reading the data once. To read a single document, we can use DocumentReference’s get() method that returns a Task<DocumentSnapshot>, while reading multiple documents from a collection, we can use Firestore Query’s get() method that returns an object of type Task<QuerySnapshot>. Both methods read the data only once.

Luckily, starting from version 19.6.0 of Firebase Realtime Database, we have the option to use a get() call too. Because of the inheritance relationship that exists between these two classes, it doesn’t really matter if we are calling get() on a DatabaseReference object or on a Query object, we’ll always read the data precisely once. This method returns an object of type Task<DataSnapshot>.

As we already know, the Firebase APIs are asynchronous. So I’ll try to explain in this article, three ways in which we can get data from Firebase Realtime Database using this new modern added get() method.

The first solution, which is some kind of an old Java habit, is using a callback, that so-called the “Hollywood Principle” which stands for “Don’t call us, we call you”. The second solution is using an Android Architecture Component called LiveData, and the third one, and the most elegant one, in my opinion, is using Kotlin Coroutines.

Let’s get started.

The database on which we are working has the following structure:

As you can see in the database schema, we have a node of products, meaning that each child within that node is a Product object. Here is the corresponding data class:

data class Product(
    var name: String? = null,
    var price: Int? = null
)

Because the result of a Firebase Realtime Database call, will always return either the products or an Exception, we’ll use a Response data class that looks like this:

data class Response(
    var products: List<Product>? = null,
    var exception: Exception? = null
)

Since the response contains one, or the other object, never both, to keep things simple, I have created in the ProductsActivity class, a print() method, that logs either the name of products or the error message:

private fun print(response: Response) {
    response.products?.let { products ->
        products.forEach{ product ->
            product.name?.let {
                Log.i(TAG, it)
            }
        }
    }

    response.exception?.let { exception ->
        exception.message?.let {
            Log.e(TAG, it)
        }
    }
}

Callback solution

Now, when it comes to the first solution, the first thing we need to create is an interface:

interface FirebaseCallback {
    fun onResponse(response: Response)
}

That contains only one abstract method that takes a single argument of type Response. With respect to the MVVM Architecture Pattern, in the ProductsRepository class, we define the “products” DatabaseReference object inside the constructor and create a method for the database call, that takes as a single argument of type FirebaseCallback:

Depending on the connection speed and state, it may take from a few hundred milliseconds to a few seconds before that data is available. When the async operation completes, the result object becomes available.

As seen above in the Response class, the first object is of type List<Products>, but the result that we get contains a List<DataSnapshot>. To be able to assign such an object in the Response class, we need to map each object of type DataSnapshot into an object of type Product. This can be done using DataSnapshot’s getValue(Class<T> valueType) method. This method is used to convert each object contained in this snapshot into an object of type Product.

If for example, the Firebase servers reject our database call, a DatabaseError will be thrown. The Exception object will be set to the second object in the Response class. In the end, we simply call the onResponse() method on the callback object by passing the response object as an argument. In this way, we can propagate the data directly to the View.

To make this mechanism work, from our ProductsViewModel class, we call the above getResponseFromRealtimeDatabaseUsingCallback() method like this:

To be able to call this method from the ProductsActivity, we need to define a ViewModel object:

private lateinit var viewModel: ProductsViewModel

And instantiate it in the onCreate():

viewModel = ViewModelProvider(this)
                           .get(ProductsViewModel::class.java)

Right after the above instantiation, we can call the following getResponseUsingCallback() method:

The mechanism is pretty simple, once the products or the Exception becomes available, the onResponse() method from the interface fires. Since it’s an abstract method, the implementation from within the anonymous class is triggered, hence the following result in the logcat:

Milk
Soy Milk
Bacon

LiveData solution

As already said, the second solution is using a LiveData object. Back to our ProductsRepository class, we create a new method called getResponseFromRealtimeDatabaseUsingLiveData() which has almost the same mechanism as above, but instead of passing the response object to a callback, we set it into a MutableLiveData object, and return it as a result of the method:

This method can be also called from within the ProductsViewModel class:

Now in the ProductsActivity class, we can observe this LiveData object using:

To print out the result, the getResponseUsingLiveData() method should also be called from within the onCreate() method. The result in the logcat is the same.

Kotlin Coroutines solution

By far the simplest, most elegant, modern, and concise solution. To get the data, this time we define a suspend function inside our ProductsRepository class:

Inside the try block, we can call get() on the DatabaseReference object and then simply call await(). We map again each object in the same way we did before. The getResponseFromRealtimeDatabaseUsingCoroutines() method is also called from within the ProductsViewModel class:

Where we emit the result further to the activity, so it can be observed using:

Calling getResponseUsingCoroutines() from within the onCreate() method, will produce the exact same result as in the previous two solutions.

Conclusion

These are the three solutions that can help us get the data from the Firebase Realtime Database using the recently added get() method. Firebase team, you really did a great job!🙏

If you wanna support me, please join me!

You can find the full source code in this GitHub repo and you can also see it on youtube:

You might also be interested in this article called How to read data from Cloud Firestore using get()?

#BetterTogether 🔥

Firebase
Firebaserealtimedatabase
Recommended from ReadMedium