Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Mobile / Android

Firebase Realtime Database By Example with Android

4.48/5 (9 votes)
13 Feb 2017CPOL12 min read 68K  
A complete, step by step tutorial on working with Firebase Realtime Database in Android

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

Java
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:Journal Entry List

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:

Image 2

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

  1. 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.
  2. 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.
  3. 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.
  4. 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:
    1. String
    2. Long
    3. Double
    4. Boolean
    5. Map<String, Object>
    6. List<Object>
  5. 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:

Java
private DatabaseReference mDatabase;

And in your onCreate() method, go ahead and instantiate that reference like this:

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

Java
private DatabaseReference journalCloudEndPoint;
 private DatabaseReference tagCloudEndPoint;

And again in the onCreate() method, you will instantiate these two references like this:

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

Java
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

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:

Java
{
  "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:

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

Java
{
  "rules": {
    ".read": true,
    ".write": true
  }
}

Re-run the app and this time, the Hello World value is added like this:

Firebase Database Hello World

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:

Java
public static List<JournalEntry> getSampleJournalEntries() {

        List<JournalEntry> journalEnrties = new ArrayList<>();
        //create the dummy journal
        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...

Java
push()

...method. The good thing is that the...

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

Java
push()

...method like this:

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

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

Image 5

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:

  1. 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.
  2. Ask – Once you have a reference to your desired path, you ask for the data by attaching a childListener or valueListener to that reference.
  3. 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

Java
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

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

  1. Firebase UI Database – provides convenient wrapper over Firebase Realtime Database
  2. Firebase UI Auth – provides convenient wrapper over Firebase Authentication
  3. 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:

Java
// Single target that includes all FirebaseUI libraries above
 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.

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)