compile 'com.google.firebase:firebase-database:10.0.1'
Write something into your database:
// Write a message to the database
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("message");
myRef.setValue("Hello World");
You can save a range of data types to the database this way, including
Java objects. When you save an object the responses from any getters
will be saved as children of this location.
Read something from your database:
To make your app data update in realtime, you should add a
ValueEventListener to the reference you just created.
The
onDataChange() method in this class is
triggered once when the listener is attached and again every time the
data changes, including the children.
// Read from the database
myRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
String value = dataSnapshot.getValue(String.class);
Log.d(TAG, "Value= " + value);
}
@Override
public void onCancelled(DatabaseError error) {
}
});
myRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
String value = dataSnapshot.getValue(String.class);
Log.d(TAG, "Value= " + value);
}
@Override
public void onCancelled(DatabaseError error) {
}
});
Configure proguard for Firebase realtime database:
# Add this global rule
-keepattributes Signature
# This rule will properly ProGuard all the model classes in
# the package com.yourcompany.models. Modify to fit the structure
# of your app.
-keepclassmembers class com.yourcompany.models.** {
*;
}
-keepattributes Signature
# This rule will properly ProGuard all the model classes in
# the package com.yourcompany.models. Modify to fit the structure
# of your app.
-keepclassmembers class com.yourcompany.models.** {
*;
}
Read and Write Data from Database:
Firebase data is written to a FirebaseDatabase reference and retrieved by attaching an asynchronous listener to the reference. The listener is triggered once for the initial state of the data and again anytime the data changes.
DatabaseReference:
To read or write data from the database, you need an instance of DatabaseReference:
private DatabaseReference mDatabase;
// ...
mDatabase = FirebaseDatabase.getInstance().getReference();
Read and write data:
Write operations:
For basic write operations, you can use setValue() to save data to a specified reference, replacing any existing data at that path. You can use this method to:
Pass types that correspond to the available JSON types as follows:
- String
- Long
- Double
- Boolean
- Map<String, Object>
- List<Object>
If you use a Java object, the contents of your object are automatically mapped to child locations in a nested fashion. Using a Java object also typically makes your code more readable and easier to maintain. For example, if you have an app with a basic user profile, your User object might look as follows:
@IgnoreExtraProperties
public class User {
public String username;
public String email;
public User() {
// Default constructor required for calls to DataSnapshot.getValue(User.class)
}
public User(String username, String email) {
this.username = username;
this.email = email;
}
}
You can add a user with setValue() as follows:
private void writeNewUser(String userId, String name, String email) {
User user = new User(name, email);
mDatabase.child("users").child(userId).setValue(user);
}
Using setValue() in this way overwrites data at the specified location, including any child nodes. However, you can still update a child without rewriting the entire object. If you want to allow users to update their profiles you could update the username as follows:
mDatabase.child("users").child(userId).child("username").setValue(name);
Listen for value events
To read data at a path and listen for changes, use the addValueEventListener() oraddListenerForSingleValueEvent() method to add a ValueEventListener to a DatabaseReference.
ValueEventListener onDataChange() Read and listen for changes to the entire contents of a path.
You can use the onDataChange() method to read a static snapshot of the contents at a given path, as they existed at the time of the event. This method is triggered once when the listener is attached and again every time the data, including children, changes. The event callback is passed a snapshot containing all data at that location, including child data. If there is no data, the snapshot returned is null.
The following example demonstrates a social blogging application retrieving the details of a post from the database:
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);
PostDetailActivity.java
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 this example, ValueEventListener also defines the onCancelled() method that is called if the read is canceled. For example, a read can be canceled if the client doesn't have permission to read from a Firebase database location. This method is passed a DatabaseError object indicating why the failure occurred.
Read data once
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.
This is useful for data that only needs to be loaded once and isn't expected to change frequently or require active listening. For instance, the blogging app in the previous examples uses this method to load a user's profile when they begin authoring a new post:
Updating or deleting data
Update specific fields
To simultaneously write to specific children of a node without overwriting other child nodes, use the updateChildren() method.
When calling updateChildren(), you can update lower-level child values by specifying a path for the key. If data is stored in multiple locations to scale better, you can update all instances of that data using data fan-out. For example, a social blogging app might have a Post class like this:
@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;
}
}
To create a post and simultaneously update it to the recent activity feed and the posting user's activity feed, the blogging application uses code like this:
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);
}
This example uses push() to create a post in the node containing posts for all users at /posts/$postid and simultaneously retrieve the key with getKey(). The key can then be used to create a second entry in the user's posts at /user-posts/$userid/$postid.
Using these paths, you can perform simultaneous updates to multiple locations in the JSON tree with a single call to updateChildren(), such as how this example creates the new post in both locations. Simultaneous updates made this way are atomic: either all updates succeed or all updates fail.
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.
Save data as transactions
When working with data that could be corrupted by concurrent modifications, such as incremental counters, you can use a transaction operation. You give this operation two arguments: an update function and an optional completion callback. The update function takes the current state of the data as an argument and returns the new desired state you would like to write. If another client writes to the location before your new value is successfully written, your update function is called again with the new current value, and the write is retried.
For instance, in the example social blogging app, you could allow users to star and unstar posts and keep track of how many stars a post has received as follows:
private void onStarClicked(DatabaseReference postRef) {
postRef.runTransaction(new Transaction.Handler() {
@Override
public Transaction.Result doTransaction(MutableData mutableData) {
Post p = mutableData.getValue(Post.class);
if (p == null) {
return Transaction.success(mutableData);
}
if (p.stars.containsKey(getUid())) {
// Unstar the post and remove self from stars
p.starCount = p.starCount - 1;
p.stars.remove(getUid());
} else {
// Star the post and add self to stars
p.starCount = p.starCount + 1;
p.stars.put(getUid(), true);
}
// Set value and report transaction success
mutableData.setValue(p);
return Transaction.success(mutableData);
}
@Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
Log.d(TAG, "postTransaction:onComplete:" + databaseError);
}
});
}
Using a transaction prevents star counts from being incorrect if multiple users star the same post at the same time or the client had stale data. If the transaction is rejected, the server returns the current value to the client, which runs the transaction again with the updated value. This repeats until the transaction is accepted or too many attempts have been made.
Write data offline
If a client loses its network connection, your app will continue functioning correctly.
Every client connected to a Firebase database maintains its own internal version of any active data. When data is written, it's written to this local version first. The Firebase client then synchronizes that data with the remote database servers and with other clients on a "best-effort" basis.
As a result, all writes to the database trigger local events immediately, before any data is written to the server. This means your app remains responsive regardless of network latency or connectivity.
Once connectivity is reestablished, your app receives the appropriate set of events so that the client syncs with the current server state, without having to write any custom code.
Work with Lists of Data on Android
This document covers working with lists of data in Firebase. To learn the basics of reading and writing Firebase data see Read and Write Data on Android.
Get a DatabaseReference
To read and write data from the database, you need an instance of DatabaseReference:
private DatabaseReference mDatabase;
// ...
mDatabase = FirebaseDatabase.getInstance().getReference();
Read and write lists
Append to a list of data
Use the push() method to append data to a list in multiuser applications. The push() method generates a unique key every time a new child is added to the specified Firebase reference. By using these auto-generated keys for each new element in the list, several clients can add children to the same location at the same time without write conflicts. The unique key generated by push() is based on a timestamp, so list items are automatically ordered chronologically.
You can use the reference to the new data returned by the push() method to get the value of the child's auto-generated key or set data for the child. Calling getKey() on a push() reference returns the value of the auto-generated key.
You can use these auto-generated keys to simplify flattening your data structure. For more information, see the data fan-out example.
Listen for child events
When working with lists, your application should listen for child events rather than the value events used for single objects.
Child events are triggered in response to specific operations that happen to the children of a node from an operation such as a new child added through the push() method or a child being updated through the updateChildren() method. Each of these together can be useful for listening to changes to a specific node in a database.
In order to listen for child events on DatabaseReference, attach a ChildEventListener:
Listener Event callback Typical usage
ChildEventListener onChildAdded() Retrieve lists of items or listen for additions to a list of items. This callback is triggered once for each existing child and then again every time a new child is added to the specified path. The DataSnapshot passed to the listener contains the the new child's data.
onChildChanged() Listen for changes to the items in a list. This event fired any time a child node is modified, including any modifications to descendants of the child node. The DataSnapshot passed to the event listener contains the updated data for the child.
onChildRemoved() Listen for items being removed from a list. The DataSnapshot passed to the event callback contains the data for the removed child.
onChildMoved() Listen for changes to the order of items in an ordered list. This event is triggered whenever the onChildChanged() callback is triggered by an update that causes reordering of the child. It is used with data that is ordered with orderByChild or orderByValue.
For example, a social blogging app might use these methods together to monitor activity in the comments of a post, as shown below:
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);
Listen for value events
While using a ChildEventListener is the recommended way to read lists of data, there are situations where attaching a ValueEventListener to a list reference is useful.
Attaching a ValueEventListener to a list of data will return the entire list of data as a single DataSnapshot, which you can then loop over to access individual children.
Even when there is only a single match for the query, the snapshot is still a list; it just contains a single item. To access the item, you need to loop over the result:
// My top posts by number of stars
myTopPostsQuery.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
// TODO: handle the post
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
});
This pattern can be useful when you want to fetch all children of a list in a single operation, rather than listening for additional onChildAdded events.
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.
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.
Sort data
To retrieve sorted data, start by specifying one of the order-by methods to determine how results are ordered:
Method Usage
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
// ...
});
This defines a query that when combined with a child listener synchronizes the client with the user's posts from the path in the database based on their user ID, ordered by the number of stars each post has received. This technique of using IDs as index keys is called data fan out, you can read more about it in Structure Your Database.
The call to the orderByChild() method specifies the child key to order the results by. In this case, posts are sorted by the value of the "starCount" child in each post. For more information on how other data types are ordered, see How query data is ordered.
Filtering data
To filter data, you can combine any of the limit or range methods with an order-by method when constructing a query.
Method Usage
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.
Unlike the order-by methods, you can combine multiple limit or range functions. For example, you can combine the startAt() and endAt() methods to limit the results to a specified range of values.
Even when there is only a single match for the query, the snapshot is still a list; it just contains a single item. To access the item, you need to loop over the result:
// My top posts by number of stars
myTopPostsQuery.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
// TODO: handle the post
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
});
Limit the number of results
You can use the limitToFirst() and limitToLast() methods to set a maximum number of children to be synced for a given callback. For example, if you use limitToFirst() to set a limit of 100, you initially only receive up to 100 onChildAdded() callbacks. If you have fewer than 100 items stored in your Firebase database, an onChildAdded() callback fires for each item.
As items change, you receive onChildAdded() callbacks for items that enter the query and onChildRemoved() callbacks for items that drop out of it so that the total number stays at 100.
The following example demonstrates how example blogging app defines a query to retrieve a list of the 100 most recent posts by all users:
// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
Query recentPostsQuery = databaseReference.child("posts")
.limitToFirst(100);
RecentPostsFragment.java
This example only defines a query, to actually synchronize data it needs to have an attached listener.
Filter by key or value
You can use startAt(), endAt(), and equalTo() to choose arbitrary starting, ending, and equivalence points for queries. This can be useful for paginating data or finding items with children that have a specific value.
How query data is ordered
This section explains how data is sorted by each of the order-by methods in the Query class.
orderByChild
When using orderByChild(), data that contains the specified child key is ordered as follows:
Children with a null value for the specified child key come first.
Children with a value of false for the specified child key come next. If multiple children have a value of false, they are sorted lexicographically by key.
Children with a value of true for the specified child key come next. If multiple children have a value of true, they are sorted lexicographically by key.
Children with a numeric value come next, sorted in ascending order. If multiple children have the same numerical value for the specified child node, they are sorted by key.
Strings come after numbers and are sorted lexicographically in ascending order. If multiple children have the same value for the specified child node, they are ordered lexicographically by key.
Objects come last and are sorted lexicographically by key in ascending order.
orderByKey
When using orderByKey() to sort your data, data is returned in ascending order by key.
Children with a key that can be parsed as a 32-bit integer come first, sorios/read-and-write#listen_for_value_eventsted in ascending order.
Children with a string value as their key come next, sorted lexicographically in ascending order.
orderByValue
When using orderByValue(), children are ordered by their value. The ordering criteria are the same as in orderByChild(), except the value of the node is used instead of the value of a specified child key.
