In this blog post, I present a complete, step by step tutorial on working with Firebase Realtime Database in Android. Few programming tasks are more challenging than creating a cloud-connected mobile app that allows users to access their application data from any device. You need advanced programming skill to handle data synchronization between client and server, create and consume APIs and/or Web Services and worry about network availability, security and data integrity.
Lately, Realm Object Server and Firebase Realtime Database has emerged as two competing solutions to creating cloud-connected mobile apps. Firebase goes a step further by incorporating other tools such as Authentication, Storage, Messaging, etc. which makes it a complete mobile platform that helps you quickly develop high-quality apps.
The challenge is that Firebase does things differently, the Firebase Realtime Database is not an offline first database like SQLite which makes it challenging for many new and established Android developers to get started with it. This tutorial will help anyone wishing to make the transition from the structured world of SQLite to the freestyle world of NoSQL in Android.
What is Firebase Realtime Database?
To the newly arrived developer, Firebase database is a cloud-hosted NoSQL database. This means that Firebase is a kind of database that does not use tables and this tableless or “non-SQL” database does not store its data (by default) locally on the device but stores them in the cloud, hence the label “Realtime”. The main benefit of this is that your users’ application data will be available from any device that they login with the same credential.
This awesome functionality implies two things, your app requires an internet connection and two you have to uniquely associate each user with their application data in the cloud. If Firebase stores data in the cloud, what happens when your application needs that data? – nothing! Your application will have to wait for an update. There is no...
findObjectById(long id)
...with Firebase, the whole Realtime, cloud synchronization magic of Firebase depends on the concept of listeners, your application should register a listener for any cloud endpoint that you want to work with the data. The data from that endpoint is returned to you once after the registration and at any other time that there is a data change.
Firebase Database Demo App
Let us create a demo app that will help us to get a better understanding of Firebase Realtime Database. We will use the humble Notepad app as our demo app, everyone creates one of these so this time, let’s call it a Dairy App. The name of this Diary App will be Pronto Diary and as the name suggests it will be used to add notes/journal. The entries can be assigned to a tag. The entries list can be filtered by their title, date or tag. Here is what the completed app will look like:
Download Source Code
Follow the steps below to complete the tutorial.
Step 1 – Create a New Android Studio Project
Create a new Android Studio project using the new project creation wizard, select the defaults and re-name the Main Activity to DiaryListActivity
, click finish.
Step 2 – Create Firebase Project
The next step after creating the Android Studio project is to add create a Firebase project since this app will be tightly coupled to Firebase. To create a Firebase project, you ought to have a Firebase account so if you have not done so already, go ahead and create one https://firebase.google.com/ and once you do, you can proceed to the console where you will create a Firebase project. Here is what the Firebase console looks like:
After you create a Firebase project, there are a couple of ways that you can create a Firebase project to Android Studio, I recommend that you use the Firebase plug in in Android Studio to create and add a project. The steps are quite easy and I explain that in this video.
Step 3: Create Model Classes
We need to create Java classes to model the objects that will be saved to Firebase Realtime database. For our demo, Diary App, the objects that will be saved are Journal Entries and the Tags that they are assigned to. At the root of the app, add a package and call it models. Inside this model package, add two Java classes called JournalEntries.java and Tag.java. Below are the class definitions of both classes.
JournalEntry.java
public class JournalEntry {
private String journalId;
private String title;
private String content;
private long dateCreated;
private long dateModified;
private String tagId;
private String tagName;
}
Tag.java
public class Tag {
private String tagId;
private String tagName;
private int journalCount;
}
Write Data to Firebase Realtime Database
Before proceeding with building our demo app, we need to ensure that we can read and write data to Firebase since the app is dependent on it. In summary, here are the steps that are involved with writing data to Firebase realtime database.
- Get a reference to Firebase database – this is analogous to calling on
getWritableDatabase()
for SQLite database or getting a Realm instance, except that this time your database is in the cloud. In simple terms, the cloud represents a massive compute capacity of which you are renting a slice. At the end of the day, all data are stored as 1s and 0s in some disk. When you call get Firebase instance, you are getting a hold of the reference to your folder in this massive disk that exists somewhere. - Refine your Firebase reference – this is analogous to getting a reference to an SQLite table or Realm managed-object. Firebase stores data in a JSON tree – aka no structure., that alone makes SQL compliant minds nervous. So, the least you can do is to dump all your data at the root of your Firebase database.
- Apply Firebase database rules – Firebase rules are similar but more than SQL constraints. It is tightly integrated with Firebase Authentication. Without the rules, anyone and everyone data will be potentially accessible to everyone. I will cover database rules in another post about Firebase Authentication. But the goal is to associate each user with their data, this way only the user’s data is pulled down to their device.
- Write data to Firebase – at some point, the data has to leave the users device en route to the nearest Google data center. This data can only travel as a JSON type which includes:
String
Long
Double
Boolean
Map<String, Object>
List<Object>
- Verify that the data made it to the cloud – after the data is saved, you can open a browser to confirm that the data made it safely to the cloud. This works during development, but thankfully the Firebase Android client makes it super easy to work with Firebase including proving
onComplete
listeners and accepting Java model classes saving you from parsing JSON to Java.
Add Seed Data to Firebase
Next, we are going to add a list of Journal Entries and Tags to the database when the app launches for the first time. We then use this data as we build out the rest of the app. But first, let use add the obligatory Hello World to Firebase.
Step 1: Get Firebase Database Reference
At the top of DiaryListActivity
, declare an instance variable for Firebase database like this:
private DatabaseReference mDatabase;
And in your onCreate()
method, go ahead and instantiate that reference like this:
mDatabase = FirebaseDatabase.getInstance().getReference();
This gets a reference to the root of your cloud database, you do not want to dump all your data at this root, so you are responsible for creating logical groupings of your data. Pretend that these are your tables, we will create one called “journalentries
” and one called “tags
”.
Step 2: Create Journal and Tag Endpoints
At the top of JournalListActivity
, declare two more instance variables like this:
private DatabaseReference journalCloudEndPoint;
private DatabaseReference tagCloudEndPoint;
And again in the onCreate()
method, you will instantiate these two references like this:
journalCloudEndPoint = mDatabase.child("journalentris");
tagCloudEndPoint = mDatabase.child("tags");
Notice that these two references are building off the root reference. These references do not exist yet, they will be created as needed by Firebase database. This is unlike SQL compliant database where you have to specify the schema beforehand. Both “journalentries
” and “tags
” will be created as branches in the root of our database. We can further create more paths or folders by appending “/
” like “journalentries/androidusers/paidusers/etc”. Firebase allows up 32 deep nesting. You can learn more about Firebase Structure here.
Step 3: Add Data to Firebase
There are two ways in which you can add data to Firebase using Firebase Android client. You can setValue()
or push()
. These will update and append data respectively. So, a good way to mistakenly wipe out your cloud data is to call setValue(“Hello World”)
on the root of your database. That will dutifully replace everything you have with Hello World.
Instead, let us go ahead and write Hello World as our first Journal Entry. Add the following line of code to towards the end of your onCreate()
method, after the Firebase references have been initialized.
journalCloudEndPoint.setValue("Hello World");
When we run the app, we expect there to be a node added to the root of our Firebase database with the name “journalentries
” and we expect the value of this node to be “Hello World
”.
Go ahead and run the application now if you are following along, if you do, you will find out that the expected results did not show up. All we have is a newly added root database like this:
Firebase Realtime Database
Why, what happened? The reason is permission. By default, a new Firebase realtime database requires you to be logged in before you can read and write. Take a look at the ruled tab of Firebase database and you will notice that authentication is required like this:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
If you want to make sure that it was Firebase database rules that is preventing us from writing to the database, you can attach an onFailed
listener to your Hello World
insert like this:
journalCloudEndPoint.setValue("Hello World").addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.d(LOG_TAG, e.getLocalizedMessage());
}
});
If you do, you get a log that read “Firebase Database error: Permission denied
”. For now, since we are focusing on learning Firebase database, we can go ahead and disable authentication and make our database public like this. Remember to click on the publish button after updating your rules. You can ignore the warnings for now.
{
"rules": {
".read": true,
".write": true
}
}
Re-run the app and this time, the Hello World value is added like this:
Step 4: Add List of Data to Firebase
Now that we have added a single value to the database, we can go ahead and add a list of data. We want to add a list of JournalEntries
, go ahead and create a few. I have added sample data to the accompanying source code like this:
public static List<JournalEntry> getSampleJournalEntries() {
List<JournalEntry> journalEnrties = new ArrayList<>();
JournalEntry journalEntry1 = new JournalEntry();
journalEntry1.setTitle("DisneyLand Trip");
journalEntry1.setContent
("We went to Disneyland today and the kids had lots of fun!");
Calendar calendar1 = GregorianCalendar.getInstance();
journalEntry1.setDateModified(calendar1.getTimeInMillis());
journalEnrties.add(journalEntry1);
}
When adding a list of data to Firebase, you use the...
push()
...method. The good thing is that the...
push()
...method returns an id that you can then set as the id aka primary key of the object that you are adding. What’s more, you can pass the object that you want to save as parameter to...
push()
...method like this:
private void addInitialDataToFirebase() {
List<JournalEntry> sampleJournalEntries = SampleData.getSampleJournalEntries();
for (JournalEntry journalEntry: sampleJournalEntries){
String key = journalCloudEndPoint.push().getKey();
journalEntry.setJournalId(key);
journalCloudEndPoint.child(key).setValue(journalEntry);
}
List<String> tagNames = SampleData.getSampleTags();
for (String name: tagNames){
String tagKey = tagCloudEndPoint.push().getKey();
Tag tag = new Tag();
tag.setTagName(name);
tag.setTagId(tagKey);
tagCloudEndPoint.child(tag.getTagId()).setValue(category);
}
}
Once you create the above method to add seed data to Firebase Realtime database, then you can update your onCreate()
method to only call that method when the app first launches, otherwise, you will flood your database with sample data. One way to do this is to insert a boolean flag to the SharedPreference
like this:
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
editor = sharedPreferences.edit();
if (sharedPreferences.getBoolean(Constants.FIRST_RUN, true)) {
addInitialDataToFirebase();;
editor.putBoolean(Constants.FIRST_RUN, false).commit();
}
In the addInitialDataToFirebase()
method above, first, we push an empty value so can get a key to that location, now armed with that key, we can create or update the object that we to save to that location. When the object is ready, we now use the setValue()
method to change the value of that location from blank to a real value. With this, you can now call this method from the onCreate()
method and after you do, the list of the data will be added to the Firebase database like this:
Read Data From Firebase Database
What goes up is expected to come down, so we need to be able to get the database back from Firebase Realtime database. We read data from Firebase database in three simple steps:
- Select – With SQLite, all reads begin with the
select
statement, a similar concept applies to Firebase database, except in this case you want to point your database reference to the exact point that you want to fetch the data from. You do not want to fetch from your root. - Ask – Once you have a reference to your desired path, you ask for the data by attaching a
childListener
or valueListener
to that reference. - Receive – You can only receive data from a Firebase database reference in two ways, first is after you attach a listener, you are returned a snapshot of the data at that reference as it is at the time that you attached the listener. Then, at any other time that the data changes.
For our demo Diary app, to get the list of Journal Entries or Tags for the cloud, we can attach a valueListener
to the respective endpoints and then handle the data snapshot like this:
List of Journal Entries
List<JournalEntry> mJournalEntries = new ArrayList<>();
journalCloudEndPoint.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot noteSnapshot: dataSnapshot.getChildren()){
JournalEntry note = noteSnapshot.getValue(JournalEntry.class);
mJournalEntries.add(note);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.d(LOG_TAG, databaseError.getMessage());
}
});
List of Tags
List<Tag> mTags = new ArrayList<>();
.tagCloudEndPoint.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot categorySnapshot: dataSnapshot.getChildren()){
Tag tag = categorySnapshot.getValue(Tag.class);
mTags.add(tag);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
Log.d(LOG_TAG, databaseError.getMessage());
}
});
Now that we have the list of data back from the cloud, you can proceed and display it using your list renderer of choice either RecyclerView
or even ListView
. You will have to manually update your list each time the data changes. To make life even easier for you, there is another library called Firebase UI that is a convenient wrapper around Firebase Realtime Database, Authentication and Storage that among other things you can use to display data from Firebase to RecyclerView
.
Working with Firebase UI Library
There are three flavors of Firebase UI Library:
- Firebase UI Database – provides convenient wrapper over Firebase Realtime Database
- Firebase UI Auth – provides convenient wrapper over Firebase Authentication
- Firebase UI Storage – provides convenient wrapper over Firebase Storage
Each of these flavors has a separate dependency that you need to add to gradle if you want to use it, or you can add all of them at once using:
compile 'com.firebaseui:firebase-ui:x.x.x'
“x.x.x
” stands for the version number. Each Firebase UI library automatically pulls in the dependency of the Firebase component it works with so if you add the dependency for Firebase UI database, then you can remove the dependency that you added earlier for Firebase Realtime database.
Firebase UI has its own implementation of RecyclerViewAdapter
called FirebaseRecyclerAdapter
. Here is an example of how we can use the FirebaseRecyclerViewAdapter
to display a list of Journal Entries.
mJournalFirebaseAdapter = new FirebaseRecyclerAdapter<JournalEntry, JournalViewHolder>(
JournalEntry.class,
R.layout.journal_custom_row,
JournalViewHolder.class,
journalCloudEndPoint) {
@Override
protected JournalEntry parseSnapshot(DataSnapshot snapshot) {
JournalEntry note = super.parseSnapshot(snapshot);
if (note != null){
note.setJournalId(snapshot.getKey());
}
return note;
}
@Override
protected void populateViewHolder
(JournalViewHolder holder, final JournalEntry journalEntry, int position) {
holder.title.setText(journalEntry.getTitle());
holder.journalDate.setText(getDueDate(journalEntry.getDateModified()));
holder.delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!TextUtils.isEmpty(journalEntry.getJournalId())) {
journalCloudEndPoint.child(journalEntry.getJournalId()).
removeValue().addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
if (mJournalFirebaseAdapter.getItemCount() < 1){
showEmptyText();
}
}
});
}
}
});
String firstLetter = journalEntry.getTitle().substring(0, 1);
ColorGenerator generator = ColorGenerator.MATERIAL;
int color = generator.getRandomColor();
TextDrawable drawable = TextDrawable.builder()
.buildRound(firstLetter, color);
holder.journalIcon.setImageDrawable(drawable);
}
};
The full implementation can be found in the accompanying source code.
Summary
This concludes this tutorial on Firebase Realtime database. If you have not already done so, please download the accompanying source code. In another post, we will continue this source code by implementing Firebase Authentication using the already added Firebase UI Auth library. If you find this post valuable, please use one of the share buttons to share.
The post Firebase Realtime Database By Example with Android appeared first on Val Okafor.