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.
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.
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:
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.
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:
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.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.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:
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:
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.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