Enabling Offline Capabilities on AndroidStay organized with collectionsSave and categorize content based on your preferences.
Firebase applications work even if your app temporarily loses its network
connection. In addition, Firebase provides tools for persisting data locally,
managing presence, and handling latency.
Disk Persistence
Firebase apps automatically handle temporary network interruptions.
Cached data is available while offline and Firebase resends any writes
when network connectivity is restored.
When you enable disk persistence, your app writes the data locally to the
device so your app can maintain state while offline, even if the user
or operating system restarts the app.
You can enable disk persistence with just one line of code.
By enabling persistence, any data that theFirebase Realtime Databaseclient
would sync while online persists to disk and is available offline,
even when the user or operating system restarts the app. This means your
app works as it would online by using the local data stored in the cache.
Listener callbacks will continue to fire for local updates.
TheFirebase Realtime Databaseclient automatically keeps a queue of all
write operations that are performed while your app is offline.
When persistence is enabled, this queue is also persisted to disk so all
of your writes are available when the user or operating system
restarts the app. When the app regains connectivity, all of
the operations are sent to theFirebase Realtime Databaseserver.
If your app usesFirebase Authentication,
theFirebase Realtime Databaseclient persists the user's authentication
token across app restarts.
If the auth token expires while your app is offline, the client pauses
write operations until your app re-authenticates the user, otherwise the
write operations might fail due to security rules.
Keeping Data Fresh
TheFirebase Realtime Databasesynchronizes and stores a local copy of the
data for active listeners. In addition, you can keep specific locations
in sync.
TheFirebase Realtime Databaseclient automatically downloads the data at
these locations and keeps it in sync even if the reference has no
active listeners. You can turn synchronization back off with the
following line of code.
By default, 10MB of previously synced data is cached. This should be
enough for most applications. If the cache outgrows its configured size,
theFirebase Realtime Databasepurges data that has been used least recently.
Data that is kept in sync is not purged from the cache.
Querying Data Offline
TheFirebase Realtime Databasestores data returned from a query for use
when offline. For queries constructed while offline,
theFirebase Realtime Databasecontinues to work for previously loaded data.
If the requested data hasn't loaded, theFirebase Realtime Databaseloads
data from the local cache. When network connectivity is available again,
the data loads and will reflect the query.
For example, this code queries for the last
four items in aFirebase Realtime Databaseof scores
Assume that the user loses connection, goes offline, and restarts the app.
While still offline, the app queries for the last two items from the
same location. This query will successfully return the last two items
because the app had loaded all four items in the query above.
In the preceding example, theFirebase Realtime Databaseclient raises
'child added' events for the highest scoring two dinosaurs, by using the
persisted cache. But it will not raise a 'value' event, since the app has
never executed that query while online.
If the app were to request the last six items while offline, it would get
'child added' events for the four cached items straight away. When the
device comes back online, theFirebase Realtime Databaseclient synchronizes
with the server and gets the final two 'child added' and the
'value' events for the app.
Handling Transactions Offline
Any transactions that are performed while the app is offline, are queued.
Once the app regains network connectivity, the transactions are sent to
theRealtime Databaseserver.
Managing Presence
In realtime applications it is often useful to detect when clients
connect and disconnect. For example, you may
want to mark a user as 'offline' when their client disconnects.
Firebase Database clients provide simple primitives that you can use to
write to the database when a client disconnects from the Firebase Database
servers. These updates occur whether the client disconnects cleanly or not,
so you can rely on them to clean up data even if a connection is dropped
or a client crashes. All write operations, including setting,
updating, and removing, can be performed upon a disconnection.
Here is a simple example of writing data upon disconnection by using theonDisconnectprimitive:
Kotlin
valpresenceRef=Firebase.database.getReference("disconnectmessage")// Write a string when this client loses connectionpresenceRef.onDisconnect().setValue("I
DatabaseReferencepresenceRef=FirebaseDatabase.getInstance().getReference("disconnectmessage");// Write a string when this client loses connectionpresenceRef.onDisconnect().setValue("I d
When you establish anonDisconnect()operation, the operation
lives on theFirebase Realtime Databaseserver. The server checks security to
make sure the user can perform the write event requested, and informs
your app if it is invalid. The server then
monitors the connection. If at any point the connection times out, or is
actively closed by theRealtime Databaseclient, the server checks security a
second time (to make sure the operation is still valid) and then invokes
the event.
Your app can use the callback on the write operation
to ensure theonDisconnectwas correctly attached:
Kotlin
presenceRef.onDisconnect().removeValue{error,reference->error?.let{Log.d(TAG,"could not establish onDisconnect event:${error.message}"
presenceRef.onDisconnect().removeValue(newDatabaseReference.CompletionListener(){@OverridepublicvoidonComplete(DatabaseErrorerror,@NonNullDatabaseReferencereference){if(error!=null){Log.d(TAG,"could not establish onDisconnect event:"+error.getMessage());}
AnonDisconnectevent can also be canceled by calling.cancel():
Kotlin
valonDisconnectRef=presenceRef.onDisconnect()onDisconnectRef.setValue("I disconnected")// ...// some time later when we change our minds// ...onDisconnectRe
OnDisconnectonDisconnectRef=presenceRef.onDisconnect();onDisconnectRef.setValue("I disconnected");// ...// some time later when we change our minds// ...onDisconnectRef
For many presence-related features, it is useful for your app
to know when it is online or offline.Firebase Realtime Databaseprovides a special location at/.info/connectedwhich
is updated every time theFirebase Realtime Databaseclient's connection state
changes. Here is an example:
/.info/connectedis a boolean value which is not
synchronized betweenRealtime Databaseclients because the value is
dependent on the state of the client. In other words, if one client
reads/.info/connectedas false, this is no
guarantee that a separate client will also read false.
On Android, Firebase automatically manages connection state to
reduce bandwidth and battery usage. When a client has no active listeners,
no pending write oronDisconnectoperations, and is not explicitly disconnected by thegoOfflinemethod,
Firebase closes the connection after 60 seconds of inactivity.
Handling Latency
Server Timestamps
TheFirebase Realtime Databaseservers provide a mechanism to insert
timestamps generated on the server as data. This feature, combined withonDisconnect, provides an easy way to reliably make note of
the time at which aRealtime Databaseclient disconnected:
Whilefirebase.database.ServerValue.TIMESTAMPis much more
accurate, and preferable for most read/write operations,
it can occasionally be useful to estimate the client's clock skew with
respect to theFirebase Realtime Database's servers. You
can attach a callback to the location/.info/serverTimeOffsetto obtain the value, in milliseconds, thatFirebase Realtime Databaseclients
add to the local reported time (epoch time in milliseconds) to estimate
the server time. Note that this offset's accuracy can be affected by
networking latency, and so is useful primarily for discovering
large (> 1 second) discrepancies in clock time.
Kotlin
valoffsetRef=Firebase.database.getReference(".info/serverTimeOffset")offsetRef.addValueEventListener(object:ValueEventListener{overridefunonDataChange(snapshot:DataSnapshot){valoffset=snapshot.getValue(Double::class.java)?:0.0valestimatedServerTimeMs=System.currentTimeMillis()+offset}overridefunonCancelled(error:DatabaseError){Log.w(TAG,"Listener was cance
DatabaseReferenceoffsetRef=FirebaseDatabase.getInstance().getReference(".info/serverTimeOffset");offsetRef.addValueEventListener(newValueEventListener(){@OverridepublicvoidonDataChange(@NonNullDataSnapshotsnapshot){doubleoffset=snapshot.getValue(Double.class);doubleestimatedServerTimeMs=System.currentTimeMillis()+offset;}@OverridepublicvoidonCancelled(@NonNullDatabaseErrorerror){Log.w(TAG,"Listener was cancell
By combining disconnect operations with connection state monitoring and
server timestamps, you can build a user presence system. In this system,
each user stores data at a database location to indicate whether or not aRealtime Databaseclient is online. Clients set this location to true when
they come online and a timestamp when they disconnect. This timestamp
indicates the last time the given user was online.
Note that your app should queue the disconnect operations before a user is
marked online, to avoid any race conditions in the event that the client's
network connection is lost before both commands can be sent to the server.
Here is a simple user presence system:
Kotlin
// Since I can connect from multiple devices, we store each connection instance separately// any time that connectionsRef's value is null (i.e. has no children) I am offlinevaldatabase=Firebase.databasevalmyConnectionsRef=database.getReference("users/joe/connections")// Stores the timestamp of my last disconnect (the last time I was seen online)vallastOnlineRef=database.getReference("/users/joe/lastOnline")valconnectedRef=database.getReference(".info/connected")connectedRef.addValueEventListener(object:ValueEventListener{overridefunonDataChange(snapshot:DataSnapshot){<valco>nnected=snapshot.getValueBoolean()?:falseif(connected){valcon=myConnectionsRef.push()// When this device disconnects, remove itcon.onDisconnect().removeValue()// When I disconnect, update the last time I was seen onlinelastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP)// Add this device to my connections list// this value could contain info about the device or a timestamp toocon.setValue(java.lang.Boolean.TRUE)}}overridefunonCancelled(error:DatabaseError){Log.w(TAG,"Listener was
// Since I can connect from multiple devices, we store each connection instance separately// any time that connectionsRef's value is null (i.e. has no children) I am offlinefinalFirebaseDatabasedatabase=FirebaseDatabase.getInstance();finalDatabaseReferencemyConnectionsRef=database.getReference("users/joe/connections");// Stores the timestamp of my last disconnect (the last time I was seen online)finalDatabaseReferencelastOnlineRef=database.getReference("/users/joe/lastOnline");finalDatabaseReferenceconnectedRef=database.getReference(".info/connected");connectedRef.addValueEventListener(newValueEventListener(){@OverridepublicvoidonDataChange(@NonNullDataSnapshotsnapshot){booleanconnected=snapshot.getValue(Boolean.class);if(connected){DatabaseReferencecon=myConnectionsRef.push();// When this device disconnects, remove itcon.onDisconnect().removeValue();// When I disconnect, update the last time I was seen onlinelastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP);// Add this device to my connections list// this value could contain info about the device or a timestamp toocon.setValue(Boolean.TRUE);}}@OverridepublicvoidonCancelled(@NonNullDatabaseErrorerror){Log.w(TAG,"Listener was ca
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2026-04-13 UTC."],[],[]]