Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - Firestore/Firebase Realtime Database ".info/connected" returns wrong connection state

I use Firestore Beta in my app and I'd like to check if there is a working online connection to the Firestore Database. The documentation says that currently there is no direct way to query the connection state, but one can use the Firebase connection state as a workaround. The code snippet for that can also be found in this part of the documentation: Build presence in Cloud Firestore.

    connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
    connectionStateListener = connectedRef.addValueEventListener(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot snapshot) {
            Boolean connected = snapshot.getValue(Boolean.class);

            if (connected == null) {
                changeState(FirebaseConnectionState.error);
                return;
            }

            if (connected) {
                changeState(FirebaseConnectionState.connected);
            } else {
                changeState(FirebaseConnectionState.not_connected);
            }
        }

        @Override
        public void onCancelled(@NonNull DatabaseError error) {
            L.i("FirebaseConnectionStateListener was cancelled");
        }
    });

The ValueEventListener itself is working and I get status updates. But for some reason the status switches from "connected" to "not connected" after 1 minute. Besides this being incorrect, I am not able to get it back to "connected" except restarting the app.

Here's some debug output:

2018-11-15 13:38:20.340: [FirebaseHeartbeatConnector] FirebaseHeartbeatConnector() called with: firebaseApp = [FirebaseApp{name=development, options=FirebaseOptions{applicationId=1:xxxxx:android:xxxxx, apiKey=xxxxx, databaseUrl=null, gcmSenderId=null, storageBucket=xxxx.appspot.com, projectId=xxxxx}}]
2018-11-15 13:38:20.480: [FirebaseHeartbeatConnector] startConnecting() called
2018-11-15 13:38:20.482: [FirebaseHeartbeatConnector] doSendFirestoreHeartbeatRequest() called
2018-11-15 13:38:20.840: [FirebaseHeartbeatConnector] change State called
2018-11-15 13:38:20.840: [FirebaseHeartbeatConnector] State changed initial -> not_connected
2018-11-15 13:38:21.380: [FirebaseHeartbeatConnector] change State called
2018-11-15 13:38:21.381: [FirebaseHeartbeatConnector] State changed not_connected -> connected
2018-11-15 13:39:20.514: [FirebaseHeartbeatConnector] change State called
2018-11-15 13:39:20.515: [FirebaseHeartbeatConnector] State changed connected -> not_connected

Why is that and how can I get a correct status update?

like image 812
FMDM Avatar asked Dec 20 '25 00:12

FMDM


1 Answers

This might be caused by Firebase closing the connection due to inactivity, which only happens on Android. From the Detecting Connection State documentation:

On Android, Firebase automatically manages connection state to reduce bandwidth and battery usage. When a client has no active listeners, no pending write or onDisconnect operations, and is not explicitly disconnected by the goOffline method, Firebase closes the connection after 60 seconds of inactivity.

Because your app isn't making real use of the Realtime Database (you're using Cloud Firestore instead), there won't be any other write operations or other listeners attached, so the connection is closed automatically. You'll likely see something like the below in logcat:

V/FA: Inactivity, disconnecting from the service

As a possible workaround, you could try attaching a listener to any node in the Realtime Database to keep the connection alive (anything will do, even if it doesn't actually exist):

FirebaseDatabase.getInstance().getReference("keepalive").addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        // Do nothing - this listener just keeps the connection alive
    }

    @Override
    public void onCancelled(@NonNull DatabaseError error) {
        Log.i("Keepalive listener was cancelled");
    }
});

Alternatively, as suggested by Frank van Puffelen, you can trick Firebase into hanging onto the connection by telling it to keep the entire Realtime Database synced, using keepSynced() on an empty reference:

FirebaseDatabase.getInstance().getReference().keepSynced(true);

You'll need to remember to remove the keepalive listener or undo keepSynced() when you actually want to mark the user's presence as disconnected, though.

Finally, with both of these examples, you still need to attach and make use of the .info/connected connection state listener to handle changes in connection state:

For many presence-related features, it is useful for your app to know when it is online or offline. Firebase Realtime Database provides a special location at /.info/connected which is updated every time the Firebase Realtime Database client's connection state changes. Here is an example:

DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot snapshot) {
    boolean connected = snapshot.getValue(Boolean.class);
    if (connected) {
      System.out.println("connected");
    } else {
      System.out.println("not connected");
    }
  }

  @Override
  public void onCancelled(DatabaseError error) {
    System.err.println("Listener was cancelled");
  }
});
like image 145
Grimthorr Avatar answered Dec 22 '25 13:12

Grimthorr



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!