Firebase Tutorial for Android: Getting Started

In this Firebase Tutorial for Android you’ll learn how to work with Realtime Databases and Authentication by creating a Joke Telling app. By Filip Babić.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 4 of 4 of this article. Click here to view the first page.

Loading Firebase…

Now that you have a user in the database, why not read it back from the database and show it in their profile? To read data, and receive snapshots, you need to use Firebase’s Value Listeners. By adding a listener to a reference you get the values from the database. You can listen to data in three ways.

First, by calling addListenerForSingleValueEvent, you read the data only once. After you receive it, the listener is removed. This is great when you need to read something once to use it in your app.

Second, using the addValueEventListener method, you listen to a certain directory and all its changes. Even if the smallest thing changes (like a user’s name) you get the entire snapshot again. This is great for showing data that isn’t large but tends to change and can benefit from a realtime update, like a user’s profile.

Lastly, with the addChildEventListener method you subscribe to changes for each child in a directory. If you change any of them, remove them or move them, you get an event for each of the mentioned cases. More importantly, it will emit each of the children one by one, the first time you attach the listener. It’s great for things like chats and feeds where new items are added often.

You’ll use the addChildEventListener for all jokes, addValueEventListener for favorite jokes and the user profile and the addListenerForSingleValueEvent for changing the joke’s liked status.

The peak of the show

There are five methods to finish up in the FirebaseDatabaseManager before your social network is ready!

Add the following code to read the jokes and the profile.

//1
override fun listenToJokes(onJokeAdded: (Joke) -> Unit) {
  database.reference.child(KEY_JOKE)
      .orderByKey()
      .addChildEventListener(object : ChildEventListener {
        override fun onCancelled(p0: DatabaseError?) = Unit
        override fun onChildMoved(p0: DataSnapshot?, p1: String?) = Unit
        override fun onChildChanged(p0: DataSnapshot?, p1: String?) = Unit
        override fun onChildRemoved(p0: DataSnapshot?) = Unit

        override fun onChildAdded(snapshot: DataSnapshot?, p1: String?) {
          snapshot?.getValue(JokeResponse::class.java)?.run {
            if (isValid()) {
              onJokeAdded(mapToJoke())
            }
          }
        }
      })
}
//2
override fun getFavoriteJokes(userId: String, onResult: (List<Joke>) -> Unit) {
  database.reference
      .child(KEY_USER)
      .child(userId)
      .child(KEY_FAVORITE)
      .addValueEventListener(object : ValueEventListener {
        override fun onCancelled(error: DatabaseError?) = onResult(listOf())

        override fun onDataChange(snapshot: DataSnapshot?) {
          snapshot?.run {
            val jokes = children.mapNotNull { it.getValue(JokeResponse::class.java) }

            onResult(jokes.map(JokeResponse::mapToJoke))
          }
        }
      })
}
//3
override fun getProfile(id: String, onResult: (User) -> Unit) {
  database.reference
      .child(KEY_USER)
      .child(id)
      .addValueEventListener(object : ValueEventListener {
        override fun onCancelled(error: DatabaseError?) = Unit

        override fun onDataChange(snapshot: DataSnapshot?) {
          val user = snapshot?.getValue(UserResponse::class.java)
          val favoriteJokes = snapshot?.child(KEY_FAVORITE)?.children
              ?.map { it?.getValue(JokeResponse::class.java) }
              ?.mapNotNull { it?.mapToJoke() }
              ?: listOf()


          user?.run { onResult(User(id, username, email, favoriteJokes)) }
        }
      })
  }
}

Let’s go through the logic behind the implementation:

  1. When listening to jokes, add a child listener to the “joke” directory. On each child, we parse the joke and add it to the list. Notice how you parse the data, by calling getValue(class) the snapshot is parsed to whatever data model you want.
  2. Favorite jokes will be stored on each user’s profile. Since the queries in the database are limited, you cannot request all jokes by ids. This is why jokes are stored on the user. You’ll read favorite jokes from each of the users’ profile directory. Every time that directory changes, you get an event, since you need to know which jokes are liked in order to show the appropriate icon in the list. The same goes for the profile, as you’re showing the number of favorite jokes there.
  3. To look up a profile, you have to call the child(KEY_USER) to enter the “users” directory and then child(id) to find a specific user. However there is more to a profile than just the user part. Since lists are actually HashMaps in Firebase, you have to manually parse each item. For that reason there is the somewhat ugly block of code for mapping all the children.

Closing up

In order to add a new joke, create a child inside the “jokes” directory, and set the value to the joke object:

override fun addNewJoke(joke: Joke, onResult: (Boolean) -> Unit) {
  val newJokeReference = database.reference.child(KEY_JOKE).push()
  val newJoke = joke.copy(id = newJokeReference.key)

  newJokeReference.setValue(newJoke).addOnCompleteListener { onResult(it.isSuccessful && it.isComplete) }
}

By calling push, you generate a new key like mentioned before. Set the value to the new joke and add a listener to be notified about completion.

Changing whether the joke is in your favorites or not is a bit trickier. You first have to know if it’s in your favorites, and then remove it if it already is, or add if it isn’t:

override fun changeJokeFavoriteStatus(joke: Joke, userId: String) {
  val reference = database.reference
      .child(KEY_USER)
      .child(userId)
      .child(KEY_FAVORITE)
      .child(joke.id)

  reference.addListenerForSingleValueEvent(object : ValueEventListener {
    override fun onCancelled(error: DatabaseError?) {}

    override fun onDataChange(snapshot: DataSnapshot?) {
      val oldJoke = snapshot?.getValue(JokeResponse::class.java)

      if (oldJoke!=null) {
        reference.setValue(null)
      } else {
        reference.setValue(joke)
      }
    }
  })
}

By listening for the value of a child, you can tell that the child doesn’t exist if the value is null – or in your case, that a joke isn’t a favorite. Same goes the other way around, if it’s not null, you know the child exists.

Build and Run the app. Go and add a new joke. You should see it in the all jokes list:

Finished App

It is not yet added to your favorite jokes, click on the heart icon to add it to favorites. Go back to the Firebase console and the Database/Data tab, and you’ll see the favorite joke in the data. If you click on the heart icon again and switch back to the console, it should be gone. The power of realtime updates! :]

Take a look at the Firebase dashboard, your database should have some data in it now.

Where to go from here

Congratulations on creating your joke-sharing social app! Wasn’t it just puntastic! You can download the final project by using the link at the top or bottom of this tutorial.

There is much more to Firebase than the Auth/Database services, try exploring every aspect of it by following the documentation.

Also check out the Firestore, the upgraded version of the database we used.

You can also read the Joe Birch article series about each of the services.

And most importantly, feel free to leave comments and suggestions in the section below! Your feedback means a lot! :]