You're viewing the legacy docs. They are deprecated as of May 18, 2016.

Java Android Guide

Saving Data

In this document we'll cover the four methods for writing data to the Firebase database: setValue(), updateChildren(), push(), and our runTransaction() feature.

Ways to Save Data

setValue( )

Write or replace data to a defined path, like messages/users/<username>

updateChildren( )

Update some of the keys for a defined path without replacing all of the data

push( )

Add to a list of data in your Firebase database. Every time you call push() Firebase generates
a unique ID, like messages/users/<unique-user-id>/<username>

runTransaction( )

Use our transactions feature when working with complex data that could be corrupted by concurrent updates

Saving Data with setValue()

The basic write operation is setValue() which saves data to the specified Firebase
reference, replacing any existing data at that path. To understand setValue(), we'll build a simple
blogging app. The data for our app will be stored at this Firebase reference:

Let's start by saving some user data to the Firebase database.
We'll create our own User class and create an object to store in the database. We'll create a reference to the location of our
user data and call setValue() to save the User object. We can pass our own custom Java
object to setValue(), provided that the class that defines it has a default constructor that takes no arguments and
public getters for the properties to be assigned:

Using setValue() will overwrite the data at the specified location, including any child nodes.

You can pass setValue() types that correspond to the the types available in JSON: String,
Long, Double, Boolean, Map<String, Object> and List<Object>.
With these types we can pass arbitrarily complex data structures, for example a Map may contain another
Map as a value.
We can duplicate the functionality above using Map instead of custom objects:

This will update Alan's data to include a nickname. If we had used setValue() here instead of updateChildren(),
it would have deleted both fullName and birthYear from our alanRef. Note that passing null in a map
will delete the data at that location in the database.

Firebase also supports multi-path updates. This means that updateChildren() can now update values at multiple locations in your Firebase database at the same time, a powerful feature which allows helps you denormalize your data. Using multi-path updates, we can add nicknames to both Alan and Grace at the same time:

Given a single key path like alanisawesome, updateChildren() only updates data at the first child level, and any data passed in beyond the first child
level is a treated as a setValue() operation. Multi-path behavior allows longer paths (like alanisawesome/nickname) to be used without overwriting data. This is why the first example differs from the second example.

Adding a Completion Callback

If you'd like to know when your data has been committed, you can add a completion listener. Both
setValue() and updateChildren() take an optional completion listener that is called when
the write has been committed to the database. If the call was unsuccessful for some reason, the listener
will be passed an error object indicating why the failure occurred:

Saving Lists of Data

When creating lists of data, it is important to keep in mind the multi-user nature of most applications and adjust
your list structure accordingly. Expanding on our example above, let's add blog posts to our app. Your first
instinct might be to use child() to store children with auto-incrementing integer indexes, like the
following:

Push vs Transaction

When working with lists of data push() ensures a unique and chronological ID. You may be tempted to use
transactions instead to generate your own IDs, but push is a far better choice. Transactions are slower and more
complex. They require one or more round trips to the server. A push ID can be generated on the client will work
while offline and is optimized for performance.

If a user adds a new post it would be stored as /posts/2. This would work if only a single author were
adding posts, but in our collaborative blogging application many users may add posts at the same time. If two
authors write to /posts/2 simultaneously, then one of the posts would be deleted by the other.

To solve this, Firebase provides a push() function that generates a unique ID every time a new
child is added to the specified Firebase reference. By using unique child names for each new element in the
list, several clients can add children to the same location at the same time without worrying about write conflicts.
The unique ID generated by push() is based on a timestamp, so list items will automatically be ordered
chronologically.

We can add posts to our blogging app with chronological, unique IDs by doing the following:

Because we used push(), Firebase generated a timestamp-based, unique ID for each blog post, and no
write conflicts will occur if multiple users create a blog post at the same time. Our data in the Firebase database now looks
like this:

Getting the Unique ID Generated by push()

Calling push() will return a reference to the new data path, which you can use to get the value of
its ID or set data to it. The following code will result in the same data as the above example, but now we'll have
access to the unique push ID that was generated:

As you can see, calling getKey() on our push() reference gives us the value of the
unique ID.

Interested in learning more about how push IDs are generated? Check out this blog post.

In the next section on Retrieving Data, we'll learn how to read data from our database.

Saving Transactional Data

When working with complex data that could be corrupted by concurrent modifications, such as incremental counters,
we provide 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 will
return the new desired state you would like to write. For example, if we wanted to increment the number of upvotes
on a specific blog post, we would write a transaction like the following:

We use currentData.getValue() != null to see if the counter is null or hasn't been
incremented yet, since transactions can be called with null if no default value was written.

If the above code had been run without a transaction function and two clients attempted to increment it
simultaneously, they would both write 1 as the new value, resulting in one increment instead of two.

Transaction Function is Called Multiple Times

doTransaction() will be called multiple times and must be able to handle null data.
Even if there is existing data in your remote database, it may not be locally cached when the transaction function is run.

Network Connectivity and Offline Writes

Every client connected to a Firebase database maintains its own internal version of any active data. When data is written, it is
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 will trigger local events immediately, before any data has even been written to
the server. This means our app will remain responsive regardless of network latency or Internet connectivity.

Once connectivity is reestablished, we'll receive the appropriate set of events so that the client "catches up"
with the current server state, without having to write any custom code.

Non-Android Runtimes

The Firebase Java client does not require the Android Runtime and can be used in any Java Runtime, including
server-side Java processes or Java desktop applications.

The Firebase Java client uses daemon threads, meaning it will not prevent a process from exiting. When running on a
non-Android runtime we may need to take extra steps to make sure that data is written to the server before our
process exits. We can attach a CompletionListener to write operations and use a Semaphore
or CountDownLatch object to prevent the process from exiting, as shown in the example below.