Firebase Realtime Database in Android

What is Firebase Realtime Database ?

The Firebase Realtime Database is a cloud-hosted database. Data is stored as JSON and synchronized in realtime to every connected client.

Instead of typical HTTP requests, the Firebase Realtime Database uses data synchronization—every time data changes, any connected device receives that update within milliseconds. Provide collaborative and immersive experiences without thinking about networking code.

The Firebase Realtime Database can be accessed directly from a mobile device or web browser; there’s no need for an application server. Security and data validation are available through the Firebase Realtime Database Security Rules, expression-based rules that are executed when data is read or written.

How to set up in Android ?

Add the SDK

First, add rules to your root-level build.gradle file, to include the google-services plugin:

buildscript {
    // ...
    dependencies {
        // ...
        classpath 'com.google.gms:google-services:3.0.0'
    }
}

Then, in your module Gradle file (usually the app/build.gradle), add the apply plugin line at the bottom of the file to enable the Gradle plugin:

// ADD THIS AT THE BOTTOM
apply plugin: 'com.google.gms.google-services'

Create Firebase project Access Firebase Console to create firebase project. Then add this project to your android application by completing this form follow each step introduction.

Add the Realtime Database to your app

Add the dependency for Firebase Realtime Database to your app-level build.gradle file:

compile 'com.google.firebase:firebase-database:9.8.1'

Next, how to structure data ?

Building a properly structured database requires quite a bit of forethought. Most importantly, you need to plan for how data is going to be saved and later retrieved to make that process as easy as possible.

All Firebase Realtime Database data is stored as JSON objects. You can think of the database as a cloud-hosted JSON tree. Unlike a SQL database, there are no tables or records. When you add data to the JSON tree, it becomes a node in the existing JSON structure with an associated key. You can provide your own keys, such as user IDs or semantic names, or they can be provided for you using push()

{  
   "users":{  
      "vPfim9T4WecleeXKfqTtQ5dPaxz2":{  
         "gender":"male",
         "name":"Nguyễn Thanh Tùng",
         "totalCheckIn":3
      }
   }
}

Work with Firebase Realtime database.

Get a DatabaseReference:

To read or write data from the database, you need an instance of DatabaseReference:

private DatabaseReference mDatabase;
// ...
mDatabase = FirebaseDatabase.getInstance().getReference();

Read & Write data:

For basic write operations, you can use setValue() to save data to a specified reference, replacing any existing data at that path.

User user = new User(“Name”, “Male”);
String userId = mDatabase.child(“users”).push().getKey();
mDatabase.child("users").child(userId).setValue(user);

To simultaneously write to specific children of a node without overwriting other child nodes, use the updateChildren() method.

@IgnoreExtraProperties
public class Post {

    public String uid;
    public String author;
    public String title;
    public String body;
    public int starCount = 0;
    public Map<String, Boolean> stars = new HashMap<>();

    public Post() {
        // Default constructor required for calls to DataSnapshot.getValue(Post.class)
    }

    public Post(String uid, String author, String title, String body) {
        this.uid = uid;
        this.author = author;
        this.title = title;
        this.body = body;
    }

    @Exclude
    public Map<String, Object> toMap() {
        HashMap<String, Object> result = new HashMap<>();
        result.put("uid", uid);
        result.put("author", author);
        result.put("title", title);
        result.put("body", body);
        result.put("starCount", starCount);
        result.put("stars", stars);

        return result;
    }

}
private void writeNewPost(String userId, String username, String title, String body) {
    // Create new post at /user-posts/$userid/$postid and at
    // /posts/$postid simultaneously
    String key = mDatabase.child("posts").push().getKey();
    Post post = new Post(userId, username, title, body);
    Map<String, Object> postValues = post.toMap();

    Map<String, Object> childUpdates = new HashMap<>();
    childUpdates.put("/posts/" + key, postValues);
    childUpdates.put("/user-posts/" + userId + "/" + key, postValues);

    mDatabase.updateChildren(childUpdates);
}

To read data at a path and listen for changes, use the addValueEventListener() or addListenerForSingleValueEvent() method to add a ValueEventListener to a DatabaseReference.

ValueEventListener postListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        // Get Post object and use the values to update the UI
        Post post = dataSnapshot.getValue(Post.class);
        // ...
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        // ...
    }
};
mPostReference.addValueEventListener(postListener);

The listener receives a DataSnapshot that contains the data at the specified location in the database at the time of the event. Calling getValue() on a snapshot returns the Java object representation of the data. If no data exists at the location, calling getValue() returns null

In some cases you may want a callback to be called once and then immediately removed, such as when initializing a UI element that you don't expect to change. You can use the addListenerForSingleValueEvent() method to simplify this scenario: it triggers once and then does not trigger again.

Delete data

The simplest way to delete data is to call removeValue() on a reference to the location of that data.

You can also delete by specifying null as the value for another write operation such as setValue()or updateChildren(). You can use this technique with updateChildren() to delete multiple children in a single API call.

Detach listeners

Callbacks are removed by calling the removeEventListener() method on your Firebase database reference.

If a listener has been added multiple times to a data location, it is called multiple times for each event, and you must detach it the same number of times to remove it completely.

Calling removeEventListener() on a parent listener does not automatically remove listeners registered on its child nodes; removeEventListener() must also be called on any child listeners to remove the callback.

Read and write lists

When working with lists, your application should listen for child events rather than the value events used for single objects.

In order to listen for child events on DatabaseReference, attach a ChildEventListener:

ChildEventListener childEventListener = new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());

        // A new comment has been added, add it to the displayed list
        Comment comment = dataSnapshot.getValue(Comment.class);

        // ...
    }

    @Override
    public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so displayed the changed comment.
        Comment newComment = dataSnapshot.getValue(Comment.class);
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onChildRemoved(DataSnapshot dataSnapshot) {
        Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so remove it.
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());

        // A comment has changed position, use the key to determine if we are
        // displaying this comment and if so move it.
        Comment movedComment = dataSnapshot.getValue(Comment.class);
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        Log.w(TAG, "postComments:onCancelled", databaseError.toException());
        Toast.makeText(mContext, "Failed to load comments.",
                Toast.LENGTH_SHORT).show();
    }
};
ref.addChildEventListener(childEventListener);

Sorting and filtering data

You can use the Realtime Database Query class to retrieve data sorted by key, by value, or by value of a child. You can also filter the sorted result to a specific number of results or a range of keys or values.

orderByChild() - Order results by the value of a specified child key. orderByKey() - Order results by child keys. orderByValue() - Order results by child values.

You can only use one order-by method at a time. Calling an order-by method multiple times in the same query throws an error.

The following example demonstrates how you could retrieve a list of a user's top posts sorted by their star count:

// My top posts by number of stars
String myUserId = getUid();
Query myTopPostsQuery = databaseReference.child("user-posts").child(myUserId)
        .orderByChild("starCount");
myTopPostsQuery.addChildEventListener(new ChildEventListener() {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
});

Filtering data

To filter data, you can combine any of the limit or range methods with an order-by method when constructing a query.

limitToFirst() - Sets the maximum number of items to return from the beginning of the ordered list of results. limitToLast() - Sets the maximum number of items to return from the end of the ordered list of results. startAt() - Return items greater than or equal to the specified key or value depending on the order-by method chosen. endAt() - Return items less than or equal to the specified key or value depending on the order-by method chosen. equalTo() - Return items equal to the specified key or value depending on the order-by method chosen.

For more detail, please visit https://firebase.google.com/docs/database/android/start/ To understand more clearly, try this quickstart https://github.com/firebase/quickstart-android/tree/master/database