Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/firebase/6.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
当新的子节点添加到正在侦听的节点时,为什么会触发Firebase实时数据库onChildChanged?_Firebase_Flutter_Dart_Firebase Realtime Database - Fatal编程技术网

当新的子节点添加到正在侦听的节点时,为什么会触发Firebase实时数据库onChildChanged?

当新的子节点添加到正在侦听的节点时,为什么会触发Firebase实时数据库onChildChanged?,firebase,flutter,dart,firebase-realtime-database,Firebase,Flutter,Dart,Firebase Realtime Database,我有2个应用程序实例在2台设备上运行。 两个设备都在使用onChildChanged方法监听相同的DB节点更改。 将新的子节点添加到侦听的节点时,onChildChanged事件仅在添加此新节点的同一设备上触发。 在第二台设备上,由于某种原因,onChildChanged事件不会触发 但是,如果我使用onchildaded方法进行监听,那么当我们正在监听的更改节点被新添加的子节点更新时,两台设备上都会正确触发此事件onchildaded 我可以接受只有在所有设备上的任何子节点实际发生更改(不是添

我有2个应用程序实例在2台设备上运行。 两个设备都在使用
onChildChanged
方法监听相同的DB节点更改。 将新的子节点添加到侦听的节点时,
onChildChanged
事件仅在添加此新节点的同一设备上触发。 在第二台设备上,由于某种原因,
onChildChanged
事件不会触发

但是,如果我使用
onchildaded
方法进行监听,那么当我们正在监听的更改节点被新添加的子节点更新时,两台设备上都会正确触发此事件
onchildaded

我可以接受只有在所有设备上的任何子节点实际发生更改(不是添加新的子节点)时才会触发
onChildChanged
。但是为什么只在执行新节点添加的设备上触发
onChildChanged

简言之,问题是: 为什么在设备上向DB添加新的子节点时,A和B设备上都会触发
onchildaded
,而
onChildChanged
仅在设备A(而不是B)上触发

相关文件:

更新:

我发现ServerValue.timestamp导致了这种行为。 如果没有ServerValue.timestamp,当我们添加新的子节点并侦听父节点更改时,onChildChanged事件从未触发。 仅当我们使用ServerValue.timestamp作为新添加子节点的字段之一时,onChildChanged事件才会发生,但仅在使用服务器时间戳值添加此新子节点的设备上发生。 我理解它最有可能被触发,因为首先使用时间戳字段占位符创建新的子节点,然后在将实际时间戳写入必填字段时,服务器会更新此节点。 所以现在的问题是:如果我们使用ServerValue.timestamp,为什么所有具有此应用程序实例的设备都不能获得onChildChanged事件?

class _MyHomePageState extends State<MyHomePage> {
  
  final String dbDataPath = 'root/chatroom0001/messages';
  DatabaseReference dbRef = FirebaseDatabase.instance.reference ();
  StreamSubscription <Event> _messagesChangedSubscription;
  List <String> _lMessages = List <String> ();

  Future <List <String>> fetchMessages (String dbDataPath) async {
    List <String> lMessages = List <String> ();
    final DataSnapshot dataMessages = await dbRef.child (dbDataPath).once ();

    dataMessages.value.forEach ((k, v) => {
      lMessages.add (v ['message'])
    });

    return lMessages;
  }

  @override
  void initState () {
    super.initState ();

    DatabaseReference messagesRef = dbRef.child (dbDataPath);

    fetchMessages (dbDataPath).then ((lMessages) {
      _lMessages = lMessages;
      setState (() {});
    });

    _messagesChangedSubscription = dbRef.child (dbDataPath).onChildChanged.listen ((event) {
      fetchMessages (dbDataPath).then ((lMessages) {
        _lMessages = lMessages;
        setState (() {});
      });
    });
  }

  @override
  void dispose () {
    super.dispose ();

    _messagesChangedSubscription.cancel ();
  }

  @override
  Widget build (BuildContext context) {
    return Scaffold (      
      body: Container (
        color: Colors.red, child: ListView.builder (
          itemCount: _lMessages.length,
          itemBuilder: (context, i) {
            return Container (padding: EdgeInsets.all (5), child: Text (_lMessages [i], style: TextStyle (fontWeight: FontWeight.bold, fontSize: 20.0)));
          }
        )
      ),
      floatingActionButton: FloatingActionButton (
        onPressed: () {
          DatabaseReference messagesRef = dbRef.child (dbDataPath);
          DatabaseReference newChatMessageRef = messagesRef.push ();
          newChatMessageRef.set ({
            'message': 'Hello Google!',
            'date': ServerValue.timestamp
          });
        },
      ),
    );
  }
}
class\u MyHomePageState扩展状态{
最终字符串dbDataPath='root/chatroom0001/messages';
DatabaseReference dbRef=FirebaseDatabase.instance.reference();
StreamSubscription _messagesChangedSubscription;
List _lMessages=List();
未来获取消息(字符串dbDataPath)异步{
List-lMessages=List();
final DataSnapshot dataMessages=wait dbRef.child(dbDataPath).once();
dataMessages.value.forEach((k,v)=>{
lMessages.add(v['message'])
});
返回消息;
}
@凌驾
void initState(){
super.initState();
DatabaseReference messagesRef=dbRef.child(dbDataPath);
获取消息(dbDataPath)。然后((lMessages){
_l消息=l消息;
setState((){});
});
_messagesChangedSubscription=dbRef.child(dbDataPath).onChildChanged.listen((事件){
获取消息(dbDataPath)。然后((lMessages){
_l消息=l消息;
setState((){});
});
});
}
@凌驾
无效处置(){
super.dispose();
_messagesChangedSubscription.cancel();
}
@凌驾
小部件构建(构建上下文){
返回脚手架(
主体:容器(
颜色:Colors.red,子项:ListView.builder(
itemCount:_lMessages.length,
itemBuilder:(上下文,i){
返回容器(padding:EdgeInsets.all(5),child:Text(_lMessages[i],style:TextStyle(fontwweight:fontwweight.bold,fontSize:20.0));
}
)
),
浮动操作按钮:浮动操作按钮(
已按下:(){
DatabaseReference messagesRef=dbRef.child(dbDataPath);
DatabaseReference newChatMessageRef=messagesRef.push();
newChatMessageRef.set({
“消息”:“你好,谷歌!”,
“日期”:ServerValue.timestamp
});
},
),
);
}
}
以下是测试项目源代码: 此代码:

  DatabaseReference newChatMessageRef = messagesRef.push ();
  newChatMessageRef.set ({
    'message': 'Hello Google!',
    'date': ServerValue.timestamp
  });
将在进行更改的客户端上导致两个写入事件:

  • 本地估计在执行此代码时立即触发本地事件
  • 然后,当客户端从服务器接收到实际值时,它将为最终值触发另一个事件
  • 侦听同一节点的远程客户端将只看到第二个事件。这可以是
    onValue
    事件,也可以是
    onchildaded
    onChildChanged
    事件,具体取决于使用的侦听器类型和本地状态


    因此,在进行更改的客户端上:如果使用
    onValue
    侦听器,您将看到两个值事件,而如果在父节点上使用
    onChild
    侦听器,您将看到本地事件的
    onchilded
    ,然后一个
    onChildChanged
    作为最终值。

    onChildChanged事件为已更改的子节点触发,在我的经验中,该事件始终按照预期的方式运行。如果您似乎没有遇到这种情况,请编辑您的问题,以显示我们中任何人都可以使用的最小代码。@FrankvanPuffelen我发现这是由ServerValue.timestamp引起的。请看我正在解释问题的这段短视频:我还用新信息更新了问题描述。@FrankvanPuffelen请使用此链接下载复制所述情况的项目源代码:谢谢你的帮助。抱歉@hellobody,但在堆栈溢出时,需要包含在问题本身中再现问题的最小代码。与外部资源的链接往往会过时,通常会导致数据过多。既然你已经发现了问题的原因,你能编辑你的问题以包含显示问题的代码吗?请注意,代码和日志输出比问题的书面描述更有可能得到答案。@FrankvanPuffelen刚刚添加了源代码。请你坐一会儿好吗