Mobile
Article

Creating a Cloud Backend for Your Android App Using Firebase

By Joyce Echessa

This post was updated in September 2016 to reflect changes in the Firebase platform.

18 months ago I wrote an article on Creating a Cloud Backend for Your Android App Using Parse. Facebook announced earlier this year that they were shutting down Parse on January 28th, 2017 and new signups to the service aren’t possible. With that surprise announcement, this forced developers relying on the service to start looking for alternatives that they could migrate their data to.

Because of this news, I thought I should revisit the topic and cover how to use a different Backend as a Service platform to manage data for your Android application.

For this tutorial, I’ll be looking at Firebase, a popular backend platform acquired by Google in October 2014.

You should always weigh the pros and cons of relying on a BaaS as opposed to building your own. Parse is not the first BaaS platform to shut down (e.g., StackMob) and won’t be the last. As a developer relying on one of these platforms, you should always be ready to migrate and have a backup plan.

With Firebase, you can store and sync data to a NoSQL cloud database. The data is stored as JSON, synced to all connected clients in realtime, and available when your app goes offline. It offers APIs that enable you to authenticate users with email and password, Facebook, Twitter, GitHub, Google, anonymous auth, or to integrate with existing authentication system. Other than the Realtime Database and Authentication, it offers a myriad of other services including Cloud Messaging, Storage, Hosting, Remote Config, Test Lab, Crash Reporting, Notification, App Indexing, Dynamic Links, Invites, AdWords, AdMob.

In this article, you’ll create a simple To Do app that will show how to save and retrieve data from Firebase, how to authenticate users, set read/write permissions on the data and validate the data on the server.

You can find the code for this project on GitHub.

Setting up the Project

To get started, create a new project named To Do. Set Minimum SDK as API 15 in the next window and select Basic Activity in the next. Click Finish on the last window, leaving the settings at their default.

Before starting the Android project, head over to firebase.google.com and create an account. After logging in to your account, head over to the Firebase console and create a project that will hold your app’s data.

Create New Project

Enter a name and country/region for the project.

Create Firebase Project

The country/region represents the country/region of your organization/company. Your selection also sets the appropriate currency for your revenue reporting. After setting a name (I used SPToDoApp) and region for the project, click on the Create Project button. This will create a project and its console will open. From the project’s console, click on the Add Firebase to your Android App option.

Firebase Options

Enter your Android project’s package name in the window that pops up. If your app is going to use certain Google Play services such as Google Sign-In, App Invites, Dynamic Links, e.t.c. then you will have to provide the SHA-1 of your signing certificate. This app won’t be using any of these services, so leave this field empty. If you wish to add these services to your app, head over to this page for information on using keytool to get the SHA-1 hash of your signing certificate. The page contains instructions for obtaining both release and debug certificate fingerprints. When done, click on the Add App button.

Add Android App to Project

Clicking on Add App will download a google-services.json file to your computer. The next page of the dialog box gives instructions of where to place the downloaded JSON file. Locate the downloaded file and move it into your Android project’s app module root directory.

Add Config File to Android Project

The JSON file contains configuration settings that the Android app needs to communicate with the Firebase servers. It contains details such as the URL to the Firebase project, the API key, etc. In the previous version of Firebase, you had to store these manually in your app’s code, but now the process has been simplified with the use of one file that contains the needed data.

If you are using version control and storing your code in a public repository, you should consider placing the google-services.json file in the .gitignore file, so that this information isn’t available to the public.

When finished, click Continue on the dialog window and you will be given further setup instructions.

Add Libraries

To your Project-level build.gradle file, add the following to the buildscript > dependencies node. This is done to include the Google services plugin for Gradle which loads the google-services.json file that you downloaded.

classpath 'com.google.gms:google-services:3.0.0'

Make sure you are editing the right gradle file.

Project Level Gradle File

Then in your App-level build.gradle file, add the following at the bottom of the file to enable the Gradle plugin.

apply plugin: 'com.google.gms.google-services'

Again, make sure it’s the right build.gradle file.

App-level Gradle File

Then add the following dependencies to the same file. Firebase has different SDKs for its features. Here you add the library needed to use the Realtime Database and the one needed for Authentication.

compile 'com.google.firebase:firebase-database:9.4.0'
compile 'com.google.firebase:firebase-auth:9.4.0'

Create an empty activity by selecting the File -> New -> Activity -> Empty Activity menu item and name it LogInActivity. Create another and name it SignUpActivity.

Change the contents of the activity_log_in.xml layout file to:

< ?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingBottom="@dimen/activity_vertical_margin"
            android:paddingLeft="@dimen/activity_horizontal_margin"
            android:paddingRight="@dimen/activity_horizontal_margin"
            android:paddingTop="@dimen/activity_vertical_margin"
            tools:context=".LoginActivity" >

    <edittext android:id="@+id/emailField"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:ems="10"
        android:inputType="textEmailAddress"
        android:hint="@string/email_hint" >

        <requestfocus></requestfocus>
    </edittext>

    <edittext android:id="@+id/passwordField"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/emailField"
        android:layout_below="@+id/emailField"
        android:ems="10"
        android:hint="@string/password_hint"
        android:inputType="textPassword"></edittext>

    <button android:id="@+id/loginButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/passwordField"
        android:layout_below="@+id/passwordField"
        android:text="@string/login_button_label"></button>

    <textview android:id="@+id/signUpText"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/loginButton"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="69dp"
        android:text="@string/sign_up_text"></textview>

</relativelayout>

Change the contents of the activity_sign_up.xml layout file to:

< ?xml version="1.0" encoding="utf-8"?>
<relativelayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingBottom="@dimen/activity_vertical_margin"
            android:paddingLeft="@dimen/activity_horizontal_margin"
            android:paddingRight="@dimen/activity_horizontal_margin"
            android:paddingTop="@dimen/activity_vertical_margin"
            tools:context=".SignUpActivity" >

    <edittext android:id="@+id/emailField"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:ems="10"
        android:inputType="textEmailAddress"
        android:hint="@string/email_hint" >

        <requestfocus></requestfocus>
    </edittext>

    <edittext android:id="@+id/passwordField"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/emailField"
        android:layout_below="@+id/emailField"
        android:ems="10"
        android:inputType="textPassword"
        android:hint="@string/password_hint"></edittext>

    <button android:id="@+id/signupButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/passwordField"
        android:layout_below="@+id/passwordField"
        android:text="@string/sign_up_button_label"></button>

</relativelayout>

These layouts are for the Login and Signup views.

Add the following to values/strings.xml.

<string name="password_hint">Password</string>
<string name="email_hint">Email</string>
<string name="sign_up_button_label">Sign Up</string>
<string name="signup_error_message">Please make sure you enter an email address and password!</string>
<string name="signup_error_title">Error!</string>
<string name="signup_success">Account successfully created! You can now Login.</string>
<string name="login_error_message">Please make sure you enter an email address and password!</string>
<string name="login_error_title">Error!</string>
<string name="login_button_label">Login</string>
<string name="sign_up_text">Sign Up!</string>
<string name="title_activity_login">Sign in</string>
<string name="add_item">Add New Item</string>
<string name="action_logout">Logout</string>

In activity_main.xml, remove the FloatingActionButton markup.

<android .support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|end"
    android:layout_margin="@dimen/fab_margin"
    android:src="@android:drawable/ic_dialog_email"></android>

And in MainActivity.java remove the FAB code there as well.

FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                .setAction("Action", null).show();
    }
});

In res/menu/menu_main.xml replace the settings item with the following Logout item.

<item android:id="@+id/action_logout"
    android:orderInCategory="100"
    android:title="@string/action_logout"
    app:showAsAction="never"></item>

Open MainActivity and replace the action_settings id with action_logout id in onOptionsItemSelected(MenuItem).

if (id == R.id.action_logout) {
    return true;
}

This menu item will logout the user.

Change content_main.xml to the below:

< ?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:tools="http://schemas.android.com/tools"
          xmlns:app="http://schemas.android.com/apk/res-auto"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:paddingLeft="@dimen/activity_horizontal_margin"
          android:paddingRight="@dimen/activity_horizontal_margin"
          android:paddingTop="@dimen/activity_vertical_margin"
          android:paddingBottom="@dimen/activity_vertical_margin"
          app:layout_behavior="@string/appbar_scrolling_view_behavior"
          tools:context=".MainActivity"
          tools:showIn="@layout/activity_main"
          android:orientation="vertical">

    <listview android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
    </listview>

    </linearlayout><linearlayout android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_gravity="bottom">
        <edittext android:id="@+id/todoText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"></edittext>
        <button android:id="@+id/addButton"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/add_item"></button>
    </linearlayout>

activity_main.xml has an include tag that points to and loads content_main.xml. The app will display the To Do items in a list view that will occupy most of the screen. At the bottom of the screen, will be an edit text field and a button to add items to the list.

With the app’s UI set up, you’ll now look at how to save and retrieve data to and from Firebase.

Security and Rules

Before retrieving and saving data from the Firebase server, you need to set up authentication and add rules that restrict access to data and validate user input before it’s saved.

Authentication

The Firebase API has built-in methods for email/password, Facebook, Twitter, GitHub, Google and anonymous authentication. This tutorial will use email and password authentication.

Head over to the Firebase console and open your project’s Dashboard by clicking on the project.

Firebase Project

If you select Database from the left panel, you will be able to see project’s data that is in JSON format.

Firebase stores all database data as JSON objects. There are no tables or records. When you add data to the JSON tree, it becomes a key in the existing JSON structure.

At the moment you can only see the root node.

No Data

If you hover over a node, you will see + and x controls that you can use to add data to the tree and delete that node respectively.

From the left panel, click on Auth and then select the Sign In Method tab on the right. Enable Email/Password authentication from the given providers.

Password and Email Authentication

In Android Studio, add the following method to MainActivity.

private void loadLogInView() {
    Intent intent = new Intent(this, LogInActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
    startActivity(intent);
}

This navigates to the Login view and clears the activity stack. This prevents the user going back to the main activity when they press the Back button from the login view.

Add the following variables to the class.

private FirebaseAuth mFirebaseAuth;
private FirebaseUser mFirebaseUser;

Then add the following to the bottom of onCreate().

// Initialize Firebase Auth
mFirebaseAuth = FirebaseAuth.getInstance();
mFirebaseUser = mFirebaseAuth.getCurrentUser();

if (mFirebaseUser == null) {
    // Not logged in, launch the Log In activity
    loadLogInView();
}

Here you check for the logged in user. If the user isn’t logged in, getCurrentUser() will return null, otherwise it will return a FirebaseUser object which will contain details of the logged in user. If the user isn’t logged in, loadLogInView() is called which redirects the user to the Login view.

Run the app and you should be redirected to the Login page.

Login

Add the following to LogInActivity.

protected EditText emailEditText;
protected EditText passwordEditText;
protected Button logInButton;
protected TextView signUpTextView;
private FirebaseAuth mFirebaseAuth;

Change the onCreate() method of the LogInActivity class to:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_log_in);

    // Initialize FirebaseAuth
    mFirebaseAuth = FirebaseAuth.getInstance();

    signUpTextView = (TextView) findViewById(R.id.signUpText);
    emailEditText = (EditText) findViewById(R.id.emailField);
    passwordEditText = (EditText) findViewById(R.id.passwordField);
    logInButton = (Button) findViewById(R.id.loginButton);

    signUpTextView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(LogInActivity.this, SignUpActivity.class);
            startActivity(intent);
        }
    });

    logInButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            String email = emailEditText.getText().toString();
            String password = passwordEditText.getText().toString();

            email = email.trim();
            password = password.trim();

            if (email.isEmpty() || password.isEmpty()) {
                AlertDialog.Builder builder = new AlertDialog.Builder(LogInActivity.this);
                builder.setMessage(R.string.login_error_message)
                        .setTitle(R.string.login_error_title)
                        .setPositiveButton(android.R.string.ok, null);
                AlertDialog dialog = builder.create();
                dialog.show();
            } else {
                mFirebaseAuth.signInWithEmailAndPassword(email, password)
                        .addOnCompleteListener(LogInActivity.this, new OnCompleteListener<authresult>() {
                            @Override
                            public void onComplete(@NonNull Task</authresult><authresult> task) {
                                if (task.isSuccessful()) {
                                    Intent intent = new Intent(LogInActivity.this, MainActivity.class);
                                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                    intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                                    startActivity(intent);
                                } else {
                                    AlertDialog.Builder builder = new AlertDialog.Builder(LogInActivity.this);
                                    builder.setMessage(task.getException().getMessage())
                                            .setTitle(R.string.login_error_title)
                                            .setPositiveButton(android.R.string.ok, null);
                                    AlertDialog dialog = builder.create();
                                    dialog.show();
                                }
                            }
                        });
            }
        }
    });
}

This initiates the view elements and the FirebaseAuth object, which is the entry point of the Firebase Authentication SDK. An event listener is added to the Sign Up text view that will open the Sign Up activity when tapped. Another event listener on the Login button performs validation on the user input, ensuring the user entered text for both fields. It then calls the Firebase server with signInWithEmailAndPassword(). The function takes the user email and password and returns a Task of AuthResult.You check if the Task is successful and redirect the user to the MainActivity, otherwise show them an error message. Below is the error message gotten from trying to login a user that hasn’t registered.

Firebase Authentication Error

You might want to show the user a better message than the one returned by task.getException().getMessage() in your app. You can check for the Exception returned to determine the error message to show the user. You can get one of the following exceptions when authentication fails:

Apart from signInWithEmailAndPassword(), you can use the following to log in the user:

  • signInWithCredential(AuthCredential) – Tries to sign in a user with the given AuthCredential. Use this method to sign in a user into your Firebase Authentication system. First retrieve the credential either directly from the user, in case of EmailAuthCredential, or from a supported authentication SDK, such as Google Sign-In or Facebook.
  • signInAnonymously() – Signs in the user anonymously without requiring any credential. This method creates a new account in your Firebase Authentication system, except in the case where there was already an anonymous user signed in into this app.
  • signInWithCustomToken(String) – Tries to sign in a user with a given Custom Token. Use this method after you retrieve a Firebase Auth Custom Token from your server, to sign in a user into your Firebase Authentication system.

With the Login functionality done, let’s set up Sign Up.

Modify SignUpActivity as shown.

package com.echessa.todo;

import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;

public class SignUpActivity extends AppCompatActivity {

    protected EditText passwordEditText;
    protected EditText emailEditText;
    protected Button signUpButton;
    private FirebaseAuth mFirebaseAuth;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sign_up);

        // Initialize FirebaseAuth
        mFirebaseAuth = FirebaseAuth.getInstance();

        passwordEditText = (EditText)findViewById(R.id.passwordField);
        emailEditText = (EditText)findViewById(R.id.emailField);
        signUpButton = (Button)findViewById(R.id.signupButton);

        signUpButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String password = passwordEditText.getText().toString();
                String email = emailEditText.getText().toString();

                password = password.trim();
                email = email.trim();

                if (password.isEmpty() || email.isEmpty()) {
                    AlertDialog.Builder builder = new AlertDialog.Builder(SignUpActivity.this);
                    builder.setMessage(R.string.signup_error_message)
                            .setTitle(R.string.signup_error_title)
                            .setPositiveButton(android.R.string.ok, null);
                    AlertDialog dialog = builder.create();
                    dialog.show();
                } else {
                    mFirebaseAuth.createUserWithEmailAndPassword(email, password)
                            .addOnCompleteListener(SignUpActivity.this, new OnCompleteListener</authresult><authresult>() {
                                @Override
                                public void onComplete(@NonNull Task</authresult><authresult> task) {
                                    if (task.isSuccessful()) {
                                        Intent intent = new Intent(SignUpActivity.this, MainActivity.class);
                                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                                        startActivity(intent);
                                    } else {
                                        AlertDialog.Builder builder = new AlertDialog.Builder(SignUpActivity.this);
                                        builder.setMessage(task.getException().getMessage())
                                                .setTitle(R.string.login_error_title)
                                                .setPositiveButton(android.R.string.ok, null);
                                        AlertDialog dialog = builder.create();
                                        dialog.show();
                                    }
                                }
                            });
                }
            }
        });
    }

}

The createUserWithEmailAndPassword() method tries to create a new user account with the given email address and password. If successful, it also signs the user into the app. A Task of AuthResult is returned with the result of the operation. You check to see if registration was successful and redirect the user to MainActivity, otherwise show them an error message. In your app, you can check for the exception thrown to decide on the error message you will show to your users. The following are the possible exceptions thrown in case of an error in creating an account.

Run the app. If you tap on the Sign Up text view on the Log In view, the Sign Up view will open.

Signup

Register an account. By default, firebase does some default validation on the data. For example, your password has to be at least 6 characters long.

Firebase Signup Error - Short Password

If you also try to sign up with an invalid email, you will get the following error.

Firebase Signup Error - Invalid Email Format

If registration is successful, you will be directed to the MainActivity. If you look at the Firebase Console, you should be able to see the created user under Auth > Users.

Firebase User

The Firebase team built an open source library called FirebaseUI that simplifies the process of adding Authentication and connecting common UI elements to the Firebase database. Read the documentation to learn more about the library.

Authorization and Data Validation

Identifying your user is only one part of security. Once you know who they are, you need a way to control their access to data in your Firebase database.

Firebase has a declarative language for specifying rules that live on the Firebase servers and determine the security of your app. You can edit them on the Database > Rules tab.

The Security Rules allow you to control access to each part of your database. By default, Firebase has security rules that require users to be authenticated.

{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}

Firebase Database Rules have a JavaScript-like syntax and come in four types:

  • .read – Describes if and when data is allowed
    d to be read by users.
  • .write – Describes if and when data is allowed to be written.
  • .validate – Defines what a correctly formatted value will look like, whether it has child attributes, and the data type.
  • .indexOn – Specifies a child to index to support ordering and querying.

.read and .write rules cascade, so the following ruleset grants read access to any data at path /foo/ (can also refer to it as the node foo) as well as any deeper paths such as /foo/bar/baz. Note that .read and .write rules shallower in the database override deeper rules, so read access to /foo/bar/baz would still be granted in this example even if a rule at the path /foo/bar/baz evaluated to false.

 {
  "rules": {
    "foo": {
      ".read": true,
      ".write": false
    }
  }
}

.validate rules do not cascade.

Modify the rules as shown and hit Publish.

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "auth != null && auth.uid == $uid",
        ".write": "auth != null && auth.uid == $uid",
        "items": {
          "$item_id": {
            "title": {
              ".validate": "newData.isString() && newData.val().length > 0"
            }
          }
        }
      }
    }
  }
}

In the above rules, auth != null && auth.uid == $uid restricts read and write permission of data on the users node (as well as its child nodes) to the user whose uid matches the id of the logged in user (auth.uid). The $uid is a variable that holds the value at that node and not the name of the node itself. With this rule, not only will the user need to be authenticated to read or write any data to the node and its children, but they will also only have access to their own data.

The Firebase Database Rules include built-in variables and functions that allow you to refer to other paths, server-side timestamps, authentication information, and more. You can use these variables and functions to build expressive rules. These variables and functions give rules power and flexibility, allowing you to refer to other paths, server-side timestamps, e.t.c.

In the apps rules, you use the built-in variable auth. This variable is populated after your user authenticates. It contains data about the user including auth.uid, a unique, alphanumeric identifier that works across providers.

The available variables are:

  • now – The current time in milliseconds since Linux epoch. This works particularly well for validating timestamps created with the SDK’s firebase.database.ServerValue.TIMESTAMP.
  • root – A RuleDataSnapshot representing the root path in the Firebase database as it exists before the attempted operation.
  • newData – A RuleDataSnapshot representing the data as it would exist after the attempted operation. It includes the new data being written and existing data.
  • data – A RuleDataSnapshot representing the data as it existed before the attempted operation.
  • $variables – A wildcard path used to represent ids and dynamic child keys.
  • auth – Represents an authenticated user’s token payload.

Firebase stores data in JSON format. In our database, each user will have an array of to-do items named items. Each item will have a title. In the above, you add some data validation that ensures that data written to /users//items must be a string greater than 0 characters long. Therefore, an item with an empty title won’t be saved.

Make sure you click on the Publish button, otherwise the rules won’t be saved. You should see a “Rules published” message.

Validation rules are great but they shouldn’t replace data validation code in your app. You should still validate input in you app to improve performance.

Saving and Retrieving Data

Add the following variables to MainActivity:

private DatabaseReference mDatabase;
private String mUserId;

Then change onCreate() as shown.

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    // Initialize Firebase Auth and Database Reference
    mFirebaseAuth = FirebaseAuth.getInstance();
    mFirebaseUser = mFirebaseAuth.getCurrentUser();
    mDatabase = FirebaseDatabase.getInstance().getReference();

    if (mFirebaseUser == null) {
        // Not logged in, launch the Log In activity
        loadLogInView();
    } else {
        mUserId = mFirebaseUser.getUid();

        // Set up ListView
        final ListView listView = (ListView) findViewById(R.id.listView);
        final ArrayAdapter<string> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, android.R.id.text1);
        listView.setAdapter(adapter);

        // Add items via the Button and EditText at the bottom of the view.
        final EditText text = (EditText) findViewById(R.id.todoText);
        final Button button = (Button) findViewById(R.id.addButton);
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                mDatabase.child("users").child(mUserId).child("items").push().child("title").setValue(text.getText().toString());
                text.setText("");
            }
        });

        // Use Firebase to populate the list.
        mDatabase.child("users").child(mUserId).child("items").addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
                adapter.add((String) dataSnapshot.child("title").getValue());
            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {
                adapter.remove((String) dataSnapshot.child("title").getValue());
            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {

            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
    }
}

You create a reference to the root node of the database with FirebaseDatabase.getInstance().getReference(). You then set a listener on the Add New Item button that saves data to Firebase when clicked.

There are four methods for writing data to the Firebase Realtime Database:

  • setValue() – Write or replace data to a defined path, such as users/<user -id>/<username>.
  • push() – Add to a list of data. Every time you call push(), Firebase generates a unique key that can also be used as a unique identifier, such as user-posts/<user -id>/<unique -post-id>.
  • updateChildren() – Update some of the keys for a defined path without replacing all of the data.
  • runTransaction() – Update complex data that could be corrupted by concurrent updates.

In our code, data is saved with:

mDatabase.child("users").child(mUserId).child("items").push().child("title").setValue(text.getText().toString());

.child gets a reference to the specified node if it exists or creates it if it doesn’t exist. The above will save the entered text at the path /users/<user id>/items/<item id>/title. .push() generates a new child location using a unique key. You use it to generate a unique key for each item added. .setValue() writes or replaces data to the defined path.

To retrieve data from Firebase, add a listener to the database reference with addChildEventListener(). You can listen for the following types of events that retrieve data:

  • ValueEventListener: onDataChange() Read and listen for changes to the entire contents of a path.
  • ChildEventListener: onChildAdded() – Retrieve lists of items or listen for additions to a list of items. Suggested use with onChildChanged() and onChildRemoved() to monitor changes to lists.
  • ChildEventListener: onChildChanged() – Listen for changes to the items in a list. Use with onChildAdded() and onChildRemoved() to monitor changes to lists.
  • ChildEventListener: onChildRemoved() – Listen for items being removed from a list. Use with onChildAdded() and onChildChanged() to monitor changes to lists.
  • ChildEventListener: onChildMoved() – Listen for changes to the order of items in an ordered list. onChildMoved() events always follow the onChildChanged() event that caused the item’s order to change (based on your current order-by method).

Our simple app only allows items to be removed or added to the list, so use onChildRemoved() and onChildAdded() respectively.

The listener receives a DataSnapshot, which is a snapshot of the data. A snapshot is a picture of the data at a particular location in a Firebase database at a single point in time. Calling getValue() on a snapshot returns the Java object representation of the data. The possible types returned by getValue() are Boolean, String, Long, Double, Map<string , Object>, and List<object>. If no data exists at the location, the snapshot will return null and it’s a good idea to check for null before you try using the data. Finally, add the retrieved data to the list view.

If you run the app and add some items, they will be added to the list and to the database.

Added Items on List View

Added Items on Firebase Console

You can add data from the Firebase console by clicking on the green + control on a node and entering the data. Just make sure you enter data in the correct format or the Android app will crash when it tries reading the data. In a production app, you should add validation to ensure it doesn’t crash when it gets the wrong data.

To add an item from the Console, click the + on the items node and add the following to the name field. You have to enter the full path //title.

/123/title

Add “This was added using the console” to the value field and hit Save.

The item will be added to the data and if you look at your Android app, the list will have updated itself with the item.

Updated Items on List View

Updated Items on Dashboard

The current app sends a String to the server. This works, but for a more complex app, your model objects will be more complex.

Firebase allows you to pass your own custom Java object to DatabaseReference.setValue() read data into an object with DataSnapshot.getValue() , provided the class that defines it has a default constructor that takes no arguments and public getters for the properties to be assigned. For more on this check the documentation.

To see this in action, create a class named Item and change it to:

package com.echessa.todo;

/**
 * Created by echessa on 8/27/16.
 */
public class Item {

    private String title;

    public Item() {}

    public Item(String title) {
        this.title = title;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

Change the Add New Item button’s on click listener to:

button.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        Item item = new Item(text.getText().toString());
        mDatabase.child("users").child(mUserId).child("items").push().setValue(item);
        text.setText("");
    }
});

This uses an Item object to save data to the database. The contents of the Item are mapped to child locations in a nested fashion. Run the app and you should still be able to add data to the list and see the saved data on the server console.

Deleting Data

Your app can now save data and retrieve it to populate a list view. Next it needs to allow users to delete items from a list and Firebase.

Add the following to the bottom of the else block in onCreate() in the MainActivity class:

// Delete items when clicked
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    public void onItemClick(AdapterView< ?> parent, View view, int position, long id) {
        mDatabase.child("users").child(mUserId).child("items")
                .orderByChild("title")
                .equalTo((String) listView.getItemAtPosition(position))
                .addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        if (dataSnapshot.hasChildren()) {
                            DataSnapshot firstChild = dataSnapshot.getChildren().iterator().next();
                            firstChild.getRef().removeValue();
                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
    }
});

This sets an onclick listener on the list view and queries the database when an item is tapped. It searches the Firebase database for the item with a title equal to the string at the tapped location. With a more complex app you might want to search for something that is unique to the object, like an id. It then removes the first occurrence of the item from the database. The listview is automatically updated.

Run the app and you will be able to delete an item from Firebase by tapping on it in the Android app.

Logging Users Out

Calling signout() invalidates a user’s token, and logs them out of your app. Change the following lines in onOptionsItemSelected(). This logs out the user and redirects them to the Login view.

if (id == R.id.action_logout) {
    mFirebaseAuth.signOut();
    loadLogInView();
}

Data Droid

In this tutorial you learned how to use Firebase to manage your Android app’s user data. This was an introductory tutorial and Firebase offers plenty of features not covered which you can explore further in the documentation.

I’d love to hear your comments, ideas and experiences using Firebase in your apps.

  • http://www.thecheesyanimation.com/Interior-Design-&-Rendering.html edie lowther

    Useful article and I think Firebase is one of them

  • http://tech.3si.vn tranhieu

    please write cloud Backend for iOS , just for funny ^^

    • http://www.echessa.com/ Joyce Echessa

      I’ll see if I can get an article on iOS done. Thanks for reading.

      • Juan Luis Sánchez

        Did they make the article for iOS?

  • Hkr Youcef

    it’s very good thenx bro

  • Victor Rodriguez

    Nice tutorial !, one silly question, how do you clear text field after insert an item?

    • http://www.echessa.com/ Joyce Echessa

      You could add the following at the bottom of the Add New Item button on click listener’s onClick() method (right after the call to Firebase).

      text.setText(“”);

  • Victor Rodriguez

    Again, super nice tutorial, I love Firebase – Android combination. I have been looking for housr, adn I can’t find how to -> Click Cloud and then select the checkbox to add Firebase to your app. This option does not appears in my Android Studio !

    • http://www.echessa.com/ Joyce Echessa

      Sorry for the late reply. You probably found the solution, but I’ll answer for anyone who might need it later. For the option, navigate to File > Project Structure from the Android Studio menu. A window will open that will have the option Cloud on its left panel. If you select Cloud, then the right panel will present the Firebase option.

      If you still can’t find it, you can do what this option does manually. All it adds is the dependency in the gradle file:

      compile ‘com.firebase:firebase-client-android:2.5.2+’

      and also adds internet permission to the manifest file:

      So you can add them yourself.

      I hope that helps.

  • JULIUS AMEH

    Thank you so much for this. I used it and all was working well but when i tried my app on a JellyBean device, i always get the error message “The specified email address is invalid”. Please help

  • Ngô Cương

    why I fill info then click button “Sign up”. it send notification “the specitied authentication provider is not enabled for this firebase”

    • Marian Vasile Caraiman

      I’m getting same error. Did you manage to solve it ? I tried from Firebase console to add the google-service.json and to add references to google-services package, without any luck. I updated my Android Studio to get latest updates from “Beta channel”, still nothing changes.

      • http://www.echessa.com/ Joyce Echessa

        Have you enabled Email/Password Sign-in provider on Firebase?

        • Marian Vasile Caraiman

          Yes, I did that. I also used the “use firebase in your Android app” that provides a json file to import in the project and make use of google-services, still without luck

          • http://www.echessa.com/ Joyce Echessa

            Then I really don’t know what could be the problem. Checking the Firebase java docs, I see the below description for the error:


            public static final FirebaseSimpleLoginErrorCode AuthenticationProviderNotEnabled

            The specified auth provider is not enabled for your Firebase. Enable it in your App Dashboard.

            But if you say you have enabled it and are still getting the bug, then I don’t know what could be up. Maybe the issue is with Firebase. At the moment, they seem to have an issue with authentication for some users on Android. The following message is on the Android docs page at the moment of writing this:

            We are currently investigating an issue affecting Firebase Authentication on some Android devices. We recommend not launching an Android app using Firebase Authentication to production until this is resolved.

            But the issue seems fairly recent and it’s not the same error you are getting:

            We are investigating an issue with Firebase Authentication on Android. Affected developers will see the FirebaseApiNotAvailableException error on some devices running Google Play Services 9.0.83.

            So basically ¯_(ツ)_/¯ :-)

            Maybe wait it out (if it’s their bug, it might get fixed) or file an issue.

          • Marian Vasile Caraiman

            thank you for looking into this. I guess it’s Firebase error and I will play with Google Play Services versions to see if I get over it.

          • RV

            Hi, Did you manage to solve this problem?

          • Marian Vasile Caraiman

            Not yet, I didn’t have time to dig more.

  • Mourad Yahia

    Thank you for this awesome tutorial ! This saved my day =) thank you !!

    • http://www.echessa.com/ Joyce Echessa

      Thanks. I’m glad it helped

  • kneth sokot

    Hi thank you for a very helpful tutorial, I learnt a lot. I have only one question, can you tell us how to upload image please? Explaining how we can do that with this To Do app.

    • http://www.echessa.com/ Joyce Echessa

      You wouldn’t upload images to Firebase directly. You can either have the images hosted on a server and save their url to Firebase with whatever object is connected to the image(s). Or you can convert the image to a base64 string and then save that to Firebase.

      If you can do the first solution, that might be preferable. You will only be storing a simple string (the url) on Firebase so you won’t run into bandwidth and storage limits as fast as with the other solution, but if you can’t host the images somewhere else, then go with the latter. Code to convert an image to base64 can be found here

      I hope that helps.

      • kneth sokot

        Thank you very much Joyce for the reply. You are so very kind. May God continue to bless you with the knowledge. :))

        • http://www.echessa.com/ Joyce Echessa

          In light of yesterday’s Google i/o keynote, I should change my answer. Google has upgraded Firebase, so now it’s possible to store images and other files on Firebase. Here is an article of the changes made to Firebase. The feature you are looking for is called Firebase Storage. Documentation can be found here

          • kneth sokot

            Thank you Joyce for those links. You are truly a blessing. Jah bless =)

  • Jendrik

    Hi,
    thanks for your great tutorial!
    Maybe you can help me. I need a server, who does stuff with the received data (json-files and/or strings). But I can´t find any examples who show me how to do that. I´ve got no idea how to build an jsp and servlet for example. Could you give me a link?

    Thanks a lot
    Jendrik

  • James Cosgrave

    Great tutorial you helped me understand a lot

  • Mahesh Battula

    Hi I need a advice. I am want to build a social app, now that Parse is shutting down, is Firebase a good choice ? Or should I any other service ? I want something which is as easy as Parse so that I can concentrate on my app. I read that using Amazon services takes time to build backend. With firebase can
    get started quickly ?

    • http://www.echessa.com/ Joyce Echessa

      Hi Mahesh,

      Sorry for the late reply. Firebase is ideal if your app doesn’t involve complex data processing. Firebase provides data storage, data validation and authentication. If your app requires complex data processing, then you should look into another service that allows you to write custom server-side code. Parse has this, it’s called Parse Code. You can write your own server-side code that does more than what the service offers. Here is a list of alternatives to Parse with brief summaries of what they offer. You might find it useful in making your decision.

      I can’t give you a definite answer without knowing the complete project spec. If it’s a simple social app, then Firebase can be sufficient. If it is complex, then it might have some requirements that will not be able to be accomplished by using Firebase alone.

  • Junie Negentien

    hello, where can i Download the whole code? I got problems in testing your tutorial. Thanks

  • Jinal Patel

    I am getting this error on sign up

    FirebaseError: Projects created at console.firebase.google.com must use the new Firebase Authentication SDKs available from firebase.google.com/docs/auth/

    • http://www.echessa.com/ Joyce Echessa

      Hi, Since writing the tutorial, Firebase underwent a whole revamp. The APIs were upgraded and a lot of the changes aren’t backwards compatible. That’s why the tutorial as it stands, produces so many bugs. I’m updating it this week, so expect working instructions later on in the week or early next week. I’ll post here again once it has been updated.

      • http://www.echessa.com/ Joyce Echessa

        The article is now up to date with the new Firebase version

    • nitish kumar

      Did u get way to solve the problem.

  • http://www.echessa.com/ Joyce Echessa

    Hi. Here is a link to that. I hope that helps.

  • Mani Teja

    that was a great tutorial, Can u plz prepare the tutorial for backend wallpaper app using Firebase

  • Narender Reddy

    Great Stuf…I have a doubt, We are saving titles under items .my doubt is how to retrive the value of second title or third title.Thanks in advance…………..

  • Vilius

    Thank you :)

  • Abdulwaheed

    Nice tutorial. How can I set onClickListener in list items and onClick some data with text and images is loaded from the Firebase.

    • praveen

      For List items we can’t write onClickListeners.
      List have onItemClickListeners.

      And if you want to write onClickListener for List item we can write in list adaptor.

    • Kinshu Mathur

      ass hole

  • Chimi Churi

    Great tutorial, thank you!!! would be nice if u add some file upload tutorial too.

  • Gill Zadji

    hi i am developing an application for an online banking on android could you please tell me if firebase is good enough to hold my database ? thank you.

    • Kinshu Mathur

      fuck you

  • James Leong

    hi,can i know how to update multiple row of data in firebase by using listview?

  • moussadiallo

    Hi, Nice tutorial, thank you

    • http://www.echessa.com/ Joyce Echessa

      Thanks

  • http://www.echessa.com/ Joyce Echessa

    There’s nothing wrong with placing a LinearLayout inside another LinearLayout. I don’t think that’s causing your error. What error message are you getting?

  • http://www.echessa.com/ Joyce Echessa

    Seems like you are out of vspace (virtual memory). Check this for a fix.

  • Leo Droidcoder

    It’s surelly not a good situation for you, but there is no reason to blame Joyce in it. You should have figured out the policies beforehand.

  • Leo Droidcoder

    Try to publish from an another country

  • http://www.echessa.com/ Joyce Echessa

    I have absolutely no control over any policies that Firebase follows. Not a shred. Neither does SitePoint. Maybe you can direct your complaint to the Firebase support staff.

Recommended
Sponsors
Because We Like You
Free Ebooks!

Grab SitePoint's top 10 web dev and design ebooks, completely free!

Get the latest in Mobile, once a week, for free.