Joao Alves

Engineering Manager at Skyscanner

Firebase Android Playground — Realtime Database

Posted at — Nov 22, 2018

image

Index:

  1. Firebase Android Playground — setting up and Analytics
  2. Firebase Android Playground — Authentication
  3. Firebase Android Playground — Realtime Database
  4. Firebase Android Playground — Cloud Messaging

Hello everyone, welcome to article number 3 on the series. In the last article, we setup our authentication system so we’re now ready to use Firebase Realtime Database to build a realtime database for our app.

What is Firebase Realtime Database

The Firebase Realtime Database is basically a cloud hosted database stored in JSON format, it offers automatic offline support and it’s synchronised in realtime with every connected client. Let’s see what that means in more detail:

Realtime Synchronisation: Firebase realtime database uses data synchronisation, so every time the data changes all connected clients get updated, no more traditional HTPP requests needed.

Automatic Offline Support: the SDK offers disk persistence for free and automatic sync when the app goes back online, so don’t need to worry about offline status. You can let your users add, edit and remove data seamlessly when they’re offline, and the SDK will take care of syncing everything properly when the app is back online.

Firebase now offers another cloud hosted database solution called Cloud Firestore. According to Firebase, this new solution is an improvement on top of Firebase Realtime Database, and it offers a more intuitive data model, and also has faster queries and can scales better. Despite all of that, it’s still in Beta, and our app is quite small, so in this article we’ll focus on the Firebase Realtime Database instead. You can check a list of the main differences between both in the Choose a Database section on the Firebase docs website.

Time to build something

The plan is to build something really simple with an even simpler and ugly UI, so please don’t look 🙈 at the layout file for the new activity we’re building 😄. As usual, let’s start with the dependencies and add the following line to our application level build.gradle script:

implementation ‘com.google.firebase:firebase-database:16.0.1’

Then, we’re going to create a simple data class to represent the data we want to save for the user, let’s use something every one likes as an example:

data class Game(
    val name: String,
    val releaseYear: Int,
    val platform: String,
    val score: Int,
    var uuid: String = ""
)

Cool, we have our Game data class and now we just need to create our simple UI to allow the user to store and visualise his games. The uuid field is there for a reason, we’ll look at it later when writing data to our realtime database.

So, for the UI, we’ll add a button in our AuthenticatedActivity to open a new RealtimeDatabaseActivity and in here the top part will be a basic form for the user to add his games, and the bottom section will be a recycler view to list those, like I said a very raw UI:

image

Enabling Realtime Database and setting Rules

Before looking into how to write and read data to/from our Firebase Realtime Database, we also need to enable it on the Firebase Console, and when doing that, it’s important to look at the rules for it. With the Firebase Realtime Database Rules we can set how and when our data can be read from and written to, amongst other things like how the data is structured and indexed. This is how the 3 most common rules look like:


// These rules don't allow anyone read or write access to your database
{
  "rules": {
    ".read": false,
    ".write": false
  }
}

// These rules give anyone, even people who are not users of your app,
// read and write access to your database
{
  "rules": {
    ".read": true,
    ".write": true
  }
}

// These rules grant access to a node matching the authenticated
// user's ID from the Firebase auth token
{
  "rules": {
    "users": {
      "$uid": {
        ".read": "$uid === auth.uid",
        ".write": "$uid === auth.uid"
      }
    }
  }
}

When enabling the realtime database on the console we have to pick if we want option 1 or option 2 to start with and we can change it later. For our simple example, we’re not going into details on how to setup proper access rules for authenticated users, so we’ll pick and stick to option 2 with public read and write access. We’ll have a shared database of games that everyone can contribute to and read from, what’s better than that? 😄 If you want to know more about to setup proper rules you can check the official documentation.

Write data

Now that we have our database created let’s go ahead and see how we can write data there using the form in our RealtimeDatabaseActivity:

button_submit_data.setOnClickListener {
    if (invalidForm()) {
        Toast.makeText(this, "All fields are mandatory", 
        	Toast.LENGTH_SHORT).show()
        return@setOnClickListener
    }

    val key = databaseReference.child("games").push().key
    val game = Game(
        name = edit_text_name.text.toString(),
        releaseYear = edit_text_release_year.text.toString().toInt(),
        platform = edit_text_platform.text.toString(),
        score = edit_text_score.text.toString().toInt()
    )
    key?.let {
        game.uuid = key
        databaseReference.child("games").child(key).setValue(game)
    }
    gamesAdapter.addGame(game)
}

Stripped the rest of the code so we can focus in the important part. The only important piece missing is how to get our databaseReference but it’s as easy as this:

databaseReference = FirebaseDatabase.getInstance().reference

With our database reference sorted, let’s analyse the rest of the code:

  1. On line 7 by using push() we’re basically adding an element on the games table on Firebase. The first time we do this, if the table doesn’t exist it just gets created. Firebase adds an element and automatically generates and return a UUID for that element that we can use later to update the value.
  2. On line 8 we basically create a game instance to whatever was submitted in the form, easy. Form validation happens in a separate method.
  3. On line 14 we just check if we have a valid key from the push() operation and then we assign the UUID to our game object and using the same key we just set the value on the database with our game.
  4. On line 18 we just tell the adapter we added a new game to the list and that’s it we’re done.

And this is how easy it is to write data, let’s go ahead and use the form to add some of my favourite games, so many hours spent on these 😄. If we go back to the Firebase Console now, we can see how the 3 games were added to our database:

image

Read data

Writing data is sorted, let’s look at how to read data from our database. With Firebase Realtime Database is really simple, it basically works using event listeners on the data, so it’s a very common callback pattern that we’re all very used to, let’s see:

databaseReference.child("games").addValueEventListener(
	object : ValueEventListener {
    override fun onCancelled(databaseError: DatabaseError) {
        Toast.makeText(this@RealtimeDatabaseActivity, 
            "Database error: $databaseError.message", 
            	Toast.LENGTH_SHORT).show()
    }

    override fun onDataChange(dataSnapshot: DataSnapshot) {
        val gamesList = dataSnapshot.children.mapNotNull { 
        	it.getValue<Game>(Game::class.java) 
        }
        gamesAdapter.setGames(gamesList)
    }
})

The code is quite self explanatory, but as with the reading part, let’s analyse it:

  1. On line 1 we add a ValueEventListener to our table reference and implement the required methods, so every time there’s a change on the games table in the database we get notified and receive a data snapshot of the database table, basically a list of Games.
  2. On line 3 we’re just showing a Toast in case of error when trying to read from the database.
  3. On line 8, we just extract the actual list of games from the DataSnapshot object and pass it to our adapter. If we implement a diff callback (which I didn’t 😃) we can handle the granular updates on our side.

And if writing was easy, reading is even more, right? This is all we need to do to be constantly listening for events on the database. In a more realistic example we should keep a reference to our ValueEventListener so we can unsubscribe when required.

If we just want to read the data once and not get all the update events we can use the addListenerForSingleValueEvent() function instead. When dealing with lists like in our case there’s also an option to get notified of events on a single child on the database table. Not relevant for our example, but if we want that we can use the addChildEventListener() function and pass an instance of ChildEventListener that will give us more granular events.

And that’s it for today, we created our real time database and integrated in the app. We now have a database to store user’s favourite games. Let me know if I’m missing out on some really cool games if you think my list is lame 😃. Hope you enjoyed the article. If you did, please don’t forget to give some 👏 and see you in a couple of weeks for a look into Firebase Cloud Messaging 👋.

Firebase Android Playground — Authentication ⇦ PREVIOUS

NEXT ⇨ Firebase Android Playground — Cloud Messaging


comments powered by Disqus