Android - Firestore/Firebase 实时数据库“.info/connected”返回错误的连接状态

Posted

技术标签:

【中文标题】Android - Firestore/Firebase 实时数据库“.info/connected”返回错误的连接状态【英文标题】:Android - Firestore/Firebase Realtime Database ".info/connected" returns wrong connection state 【发布时间】:2019-04-18 14:47:36 【问题描述】:

我在我的应用中使用 Firestore Beta,我想检查是否存在与 Firestore 数据库的有效在线连接。文档说目前没有直接的方法来查询连接状态,但是可以使用 Firebase 连接状态作为一种解决方法。代码 sn-p 也可以在这部分文档中找到: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");
        
    );

ValueEventListener 本身正在工作,我会收到状态更新。但由于某种原因,状态在 1 分钟后从“已连接”切换为“未连接”。除了这不正确之外,我无法将其恢复为“已连接”,除非重新启动应用程序。

这是一些调试输出:

2018-11-15 13:38:20.340: [FirebaseHeartbeatConnector] FirebaseHeartbeatConnector() called with: firebaseApp = [FirebaseAppname=development, options=FirebaseOptionsapplicationId=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

为什么会这样?如何获得正确的状态更新?

【问题讨论】:

在 Android 上,Firebase 会自动管理连接状态以减少带宽和电池使用量。当客户端没有活动的侦听器、没有挂起的写入或onDisconnect 操作,并且没有通过goOffline 方法显式断开连接时,Firebase 会在 60 秒不活动后关闭连接。阅读Detecting Connection State 文档的更多信息。 如果您没有将 Firebase 实时数据库用于连接状态以外的任何其他内容,您可能需要添加一个空的连接侦听器。如果添加FirebaseDatabase.getInstance().keepSynced(true); 会有所不同,您可以试试吗? @FrankvanPuffelen 好主意,但这会引发 DatabaseException: Can't call keepSynced() on .info paths。 @Grimthorr 如果我的要求解决了问题,那么确实需要在文档中进行更新。 @FrankvanPuffelen 啊哈,我没有意识到 keepSynced(true) 创建了一个空侦听器,这是一个巧妙的小技巧,无需手动创建。我刚刚对此进行了测试,仅使用FirebaseDatabase.getInstance().getReference().keepSynced(true); 确实可以阻止它在 60 秒后断开连接。 【参考方案1】:

这可能是由于 Firebase 因不活动而关闭连接造成的,这种情况仅在 Android 上发生。来自Detecting Connection State 文档:

在 Android 上,Firebase 会自动管理连接状态以减少带宽和电池使用量。当客户端没有活动的侦听器、没有挂起的写入或onDisconnect 操作,并且没有通过goOffline 方法显式断开连接时,Firebase 会在 60 秒不活动后关闭连接。

由于您的应用并未真正使用实时数据库(您使用的是 Cloud Firestore),因此不会附加任何其他写入操作或其他侦听器,因此连接会自动关闭。您可能会在 logcat 中看到类似以下内容:

V/FA: Inactivity, disconnecting from the service

作为一种可能的解决方法,您可以尝试将侦听器附加到实时数据库中的任何节点以保持连接处于活动状态(任何方法都可以,即使它实际上不存在):

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");
    
);

或者,正如Frank van Puffelen 所建议的那样,您可以通过在空引用上使用keepSynced() 告诉Firebase 保持整个实时数据库同步来欺骗Firebase 挂起连接:

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

不过,当您真正想将用户的状态标记为已断开连接时,您需要记住移除 keepalive 侦听器或撤消 keepSynced()

最后,对于这两个示例,您仍然需要附加并使用.info/connected connection state listener 来处理连接状态的变化:

对于许多与状态相关的功能,让您的应用知道它何时在线或离线很有用。 Firebase 实时数据库在 /.info/connected 提供了一个特殊位置,每次 Firebase 实时数据库客户端的连接状态更改时都会更新该位置。这是一个例子:

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");
  
);

【讨论】:

我试过这个,但是 valueeventlistener 没有收到任何回调。我提出了进一步讨论的要点:gist.github.com/fm-eb/d629a7e236a0066624696c9561aaf8ab 那是因为您的实时数据库中不存在任何内容;只需要这个监听器来保持连接处于活动状态。您仍然需要在原始问题中使用 .info/connected 侦听器。 最好的方法是使用FirebaseDatabase.getInstance().getReference().keepSynced(true); 来阻止连接超时。同样,您仍然需要附加 .info/connected 侦听器来实际监控连接状态。 嗨,我评论了要点。没有此更改的事件,我仍然得到 60 秒超时。 (1/2) 这真的很奇怪。我复制/粘贴了您的代码,它工作了一次。然后我将我的 firebaseapp-instance 添加为 FirebaseDatabase.getInstance() 的参数,但它不再起作用(1 分钟超时)。然后我删除了firebaseapp-instance,它也不再工作了......然后我完全卸载了该应用程序并再次返回您的代码=>不再工作......

以上是关于Android - Firestore/Firebase 实时数据库“.info/connected”返回错误的连接状态的主要内容,如果未能解决你的问题,请参考以下文章

Android 逆向Android 权限 ( Android 逆向中使用的 android.permission 权限 | Android 系统中的 Linux 用户权限 )

android 21 是啥版本

Android逆向-Android基础逆向(2-2)

【Android笔记】android Toast

图解Android - Android核心机制

Android游戏开发大全的目录