Mobile
Article

Creating a Cloud Backend for Your Android App Using Firebase

By Joyce Echessa

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. It also offers hosting for static assets and offers SSL certificates.

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.

The code for this project can be found 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 Blank 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.com and create an account. After logging in to your account, create a Firebase app that will hold your app’s data. Go to your account page and enter a name for your app in the prompt on that page.

Create New App

Firebase will generate an App URL from the name you enter. You will be notified if the name is already registered. You need to use something unique. I used SPToDoApp and so the app’s base url was https://sptodoapp.firebaseio.com.

Created App

Head back to Android Studio and create a new class called Constants. Change its contents to:

package com.echessa.todo; // Change to match your package name

/**
 * Created by echessa on 3/10/16.
 */
public class Constants {
    public static final String FIREBASE_URL = "https://cwtodoapp.firebaseio.com"; // Replace with your own URL
}

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_login.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 />
    </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" />

    <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" />

    <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" />

</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 />
    </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" />

    <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" />

</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"/>

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"/>

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 log out 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
        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"/>
        <Button
            android:id="@+id/addButton"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/add_item"/>
    </LinearLayout>

</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 UI set up, now include the Firebase SDK into the app. You can do this manually, but Android Studio has a tool that makes the process faster.

In Android Studio select File -> Project Structure. Click Cloud and then select the checkbox to add Firebase to your app.

Add Firebase Library

This adds a dependency to build.gradle, and adds internet permission to the manifest file. The wizard doesn’t always include the latest version of the Firebase SDK in the gradle file, so you might want to add it manually or update the gradle file. Check for the latest version in this quickstart document.

Add the following to the android{} section in the build.gradle file (the gradle file mentioned throughout is the Module:app gradle file).

    packagingOptions {
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE-FIREBASE.txt'
        exclude 'META-INF/NOTICE'
    }

This prevents a build error that you might have received about duplicate files.

The Firebase library must be initialized once with an Android context. This must happen before any Firebase app reference is created or used. You can add the setup code to the Application or Activity onCreate method. This tutorial will use the Application class option.

Add a new class to the project called ToDoApplication and make it a subclass of Application. Change the class to:

package com.echessa.todo;

import android.app.Application;
import com.firebase.client.Firebase;

/**
 * Created by echessa on 3/10/16.
 */
public class ToDoApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        Firebase.setAndroidContext(this);
    }

}

Open the AndroidManifest.xml file and add the following attribute to the application tag.

android:name=".ToDoApplication"

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. You can also integrate it with your existing login server. This tutorial will use email and password authentication.

Head over to your Firebase account and open your app’s Dashboard by clicking the Manage App button.

Firebase App

This takes you to the Data tab of the Dashboard where you are able to view the app’s data that is in JSON format.

All Firebase database data is stored 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.

No Data Hover

Open the Login & Auth tab and check the Enable Email & Password Authentication checkbox under the Email & Password tab. You should see a Saved successfully message. The tab displays a form that you can use to set up a Password Reset email. At the bottom of the tab is a list that displays registered users.

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 variable to the class.

private Firebase mRef;

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

// Check Authentication
mRef = new Firebase(Constants.FIREBASE_URL);
if (mRef.getAuth() == null) {
    loadLoginView();
}

Here you create a Firebase reference pointing to you app’s url, and then call getAuth() to check if the user is authenticated.

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;

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

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

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

    final Firebase ref = new Firebase(Constants.FIREBASE_URL);

    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 {
                final String emailAddress = email;

                //Login with an email/password combination
                ref.authWithPassword(email, password, new Firebase.AuthResultHandler() {
                    @Override
                    public void onAuthenticated(AuthData authData) {
                        // Authenticated successfully with payload authData
                        Map<String, Object> map = new HashMap<String, Object>();
                        map.put("email", emailAddress);
                        ref.child("users").child(authData.getUid()).setValue(map);

                        Intent intent = new Intent(LoginActivity.this, MainActivity.class);
                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                        startActivity(intent);
                    }

                    @Override
                    public void onAuthenticationError(FirebaseError firebaseError) {
                        // Authenticated failed with error firebaseError
                        AlertDialog.Builder builder = new AlertDialog.Builder(LoginActivity.this);
                        builder.setMessage(firebaseError.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 creates a Firebase reference. 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 shows the progress indicator and calls the Firebase server with authWithPassword(). The function takes the user email and password and a AuthResultHandler handler object.

The handler must implement two methods. These are onAuthenticated(), called if the authentication is successful and onAuthenticationError(), called when there is a failure.

Finally authenticate the user and store data at the path https://<YOUR-FIREBASE-APP>.firebaseio.com/users/<uid>, where <uid> represents the unique id obtained from the authentication data.

Map<String, Object> map = new HashMap<String, Object>();
map.put("email", emailAddress);
ref.child("users").child(authData.getUid()).setValue(map);

This isn’t a requirement for this app. You could just redirect the user to the main activity, but I included it here to show how data can be added to a user’s profile. When you authenticate a user, and it’s their first time logging in, no profile or user state is stored in the Firebase database. If you would like this data persisted to the database you must do so explicitly using the above method. The AuthResultHandler returns an AuthData object that contains unique information about the logged in user.

Run the app and try logging in. There are no registered users stored in the database, so login will fail with a The specified user does not exist error message. By default, the API offers some validation of the user data. Enter an invalid email and you will see an The specified email is invalid error message.

If you tap on the Sign Up text view, the Sign Up view will open.

Signup

Next is user creation. Change SignUpActivity to:

package com.echessa.todo;

import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
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.firebase.client.Firebase;
import com.firebase.client.FirebaseError;

public class SignUpActivity extends AppCompatActivity {

    protected EditText passwordEditText;
    protected EditText emailEditText;
    protected Button signUpButton;

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

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

        final Firebase ref = new Firebase(Constants.FIREBASE_URL);

        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 {

                    // signup
                    ref.createUser(email, password, new Firebase.ResultHandler() {
                        @Override
                        public void onSuccess() {
                            AlertDialog.Builder builder = new AlertDialog.Builder(SignUpActivity.this);
                            builder.setMessage(R.string.signup_success)
                                    .setPositiveButton(R.string.login_button_label, new DialogInterface.OnClickListener() {
                                        @Override
                                        public void onClick(DialogInterface dialogInterface, int i) {
                                            Intent intent = new Intent(SignUpActivity.this, LoginActivity.class);
                                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                            intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
                                            startActivity(intent);
                                        }
                                    });
                            AlertDialog dialog = builder.create();
                            dialog.show();
                        }

                        @Override
                        public void onError(FirebaseError firebaseError) {
                            AlertDialog.Builder builder = new AlertDialog.Builder(SignUpActivity.this);
                            builder.setMessage(firebaseError.getMessage())
                                    .setTitle(R.string.signup_error_title)
                                    .setPositiveButton(android.R.string.ok, null);
                            AlertDialog dialog = builder.create();
                            dialog.show();
                        }
                    });
                }
            }
        });
    }
}

The createUser() method creates an account, taking a ResultHandler argument which implements two callback functions. These are onSuccess(), called when the account is successfully created and onError(), called when an error occurs. When an account is created, the user is not automatically logged in, but directed to the Login view.

Run the app and register an account. If registration is successful, you will see a prompt that will then direct you to the Login view. On the web Dashboard, you should see the registered user’s details in the Login & Auth tab.

Registered User

Login using your credentials and you will be directed to the MainActivity. If you look at the web dashboard, under the Data tab, you should see details of the created user.

User Details

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

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 by selecting a Firebase app in the Account Dashboard and viewing the Security & Rules tab.

These Security and Firebase Rules allow you to control access to each part of your database. Rules applied to a node in your database cascade to all its children.

In the app are the following rules.

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

This gives read and write permissions to the root node (and thus every child node) to anyone. Change the rules to the below:

{
    "rules": {
      "users": {
        "$uid": {
          ".read": "auth != null && auth.uid == $uid",
          ".write": "auth != null && auth.uid == $uid"
        }
      }
    }
}

This restricts read and write permission of data on the https://<YOUR-FIREBASE-APP>.firebaseio.com/users/<uid> path 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.

The Security and Firebase Rules include built-in variables and functions. 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, and more.

The most important built-in variable is auth. This variable is populated after your user authenticates. It contains data about the user and auth.uid, a unique, alphanumeric identifier that works across providers.

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

Data Validation

Every Firebase application includes a schema-less database. This makes it easy to change things as you develop, but once your app is ready to distribute, it’s important for data to stay consistent. The rules language includes a .validate rule. Use it to specify declarative validation rules just like .read and .write rules. The only difference is that validation rules do not cascade.

Change the app’s rules as shown and save them.

{
    "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"
                }
            }
          }
        }
      }
    }
}

This adds a rule that enforces that data written to /users//items must be a string greater than 0 characters long. This prevents saving empty strings. You still haven’t added the items node to the data object, this holds the user’s to do list items and will happen later.

Validation rules are great but they shouldn’t replace data validation code in your app. You should still validate input for best performance and to provide the best experience when your users are offline.

Saving and Retrieving Data

Add the following variables to MainActivity:

private String mUserId;
private String itemsUrl;

Then add the following to the bottom of the onCreate() after the code that checks for authentication.

try {
        mUserId = mRef.getAuth().getUid();
    } catch (Exception e) {
        loadLoginView();
    }

    itemsUrl = Constants.FIREBASE_URL + "/users/" + mUserId + "/items";

    // 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) {
            new Firebase(itemsUrl)
                    .push()
                    .child("title")
                    .setValue(text.getText().toString());
        }
    });

    // Use Firebase to populate the list.
    new Firebase(itemsUrl)
            .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(FirebaseError firebaseError) {

                }
            });

This adds the user’s id to create a path that saves the to do items to (itemsUrl). Data is saved with:

new Firebase(itemsUrl)
  .push()
  .child("title")
  .setValue(text.getText().toString());

This creates a Firebase reference that points to the /users//items path. Firebase.push() generates a new child location using a unique key and returns a Firebasereference to it. Firebase.child() gets a Firebase reference for the location at the specified relative path and Firebase.setValue() writes or replaces data to the defined path.

Retrieve the data by adding a listener to the Firebase reference using addChildEventListener(). This method is called anytime new data is added to our Firebase reference.

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 Dashboard

You can add data from the Firebase Dashboard 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 Dashboard, click the + on the items node and add the following to the name field. You have to enter the full path //title.

/DashItem/title

Add This was added using the dashboard to the value field.

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 setValue(), provided the class that defines it has a default constructor that takes no arguments and public getters for the properties to assigned.

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

package com.echessa.todo;

/**
 * Created by echessa on 3/10/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());
        new Firebase(itemsUrl)
                .push()
                .setValue(item);
    }
});

This instead 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 dashboard.

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 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) {
            new Firebase(itemsUrl)
                    .orderByChild("title")
                    .equalTo((String) listView.getItemAtPosition(position))
                    .addListenerForSingleValueEvent(new ValueEventListener() {
                        public void onDataChange(DataSnapshot dataSnapshot) {
                            if (dataSnapshot.hasChildren()) {
                                DataSnapshot firstChild = dataSnapshot.getChildren().iterator().next();
                                firstChild.getRef().removeValue();
                            }
                        }

                        public void onCancelled(FirebaseError firebaseError) {
                        }
                    });
        }
    });

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 unauth() 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) {
    mRef.unauth();
    loadLoginView();
}

The app is now functional but has a serious bug. When a user logs out and logs back in, their To Do Items are deleted from the server. The problem lies in the LoginActivity class in the following statements.

Map<String, Object> map = new HashMap<String, Object>();
map.put("email", emailAddress);
ref.child("users").child(authData.getUid()).setValue(map);

This creates an email node under the /users/<user-id>/ node. The problem is that setValue() writes data to the server, overwriting any data at the specified location and all child locations. Thus all the nodes under /users/<user-id>/ are lost every time the user logs in. Instead a method is needed that performs an update if the data exists and creates it if not.

Change the last statement to:

ref.child("users").child(authData.getUid()).updateChildren(map);

Now run the app and you should be able to log in and out without losing your data.

Conclusion

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.

Joyce Echessa
Meet the author
I am a web developer who dabbles in mobile development from time to time. You can find me on Twitter @joyceechessa to see what I’m up to.
  • 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.

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

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

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