Firebase子侦听器回调返回一个值,第二个值返回null

时间:2018-10-09 20:00:15

标签: android firebase firebase-realtime-database

在为Android运行Firebase实时数据库时,我面临非常奇怪的结果。 主要问题是:

  1. 当我尝试添加具有两个字段的子项时,onChildAdded方法将为第一个键返回插入的值,为第二个键返回null,并且不会为新插入的子项的第二个值调用。
  2. 当我尝试使用valueEventListener进行相同的操作时,每次创建新的子项时,都会两次调用onDataChange方法。在第一个回调中,返回的一个字段的值和第二个值为null,在第二个回调中,返回两个更新的值。

我的Firebase数据库中具有这种结构。

{
  "users" : {
    "-LOOlXqtc0XK3ZXLKFc6" : {
      "Age" : 50,
      "Name" : "Ali"
    },
    "-LOPIfgGQMhyMkRMcpTb" : {
      "Age" : 80,
      "Name" : "New Name"
    }
  }
}

这是我的MainActivity代码:

mDatabase = FirebaseDatabase.getInstance();
mRef = mDatabase.getReference("users");

在单击“插入数据”按钮时执行此代码。

private void runCode(View view){
        String name = mInputText.getText().toString();
        int age= Integer.parseInt(mInputNum.getText().toString());

        String key = mRef.push().getKey();
        mRef.child(key).child("Name").setValue(name);
        mRef.child(key).child("Age").setValue(age);
}

这是使用valueEventListener读取数据按钮的click方法。

private void readData(View view) {
        //read data here 
        //this read data method has value listener not child listener
        mRef.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                Person data=  dataSnapshot.getValue(Person.class);
                Map<String,Object> data1= (Map<String, Object>) dataSnapshot.getValue();

                Log.d(TAG, "onChildAdded: Name: "+data1.get("Name"));
                Log.d(TAG, "onChildAdded: Age: "+data1.get("Age"));
                Log.d(TAG, "This is Person: "+data);
            }
            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {
            }
        });
}

logcat的输出是这样的: 这是我第一次单击“读取数据”方法。

D/MyTag: onChildAdded: Name: Ali
D/MyTag: onChildAdded: Age: 50
D/MyTag: This is Person: Person{Name='Ali', Age=50}

这是当我插入一个名称为“ New Name”且年龄为80的新孩子时的logcat输出。

D/MyTag: onChildAdded: Name: Ali
D/MyTag: onChildAdded: Age: 50
D/MyTag: This is Person: Person{Name='Ali', Age=50}
D/MyTag: onChildAdded: Name: New Name
D/MyTag: onChildAdded: Age: null
D/MyTag: This is Person: Person{Name='New Name', Age=0}
D/MyTag: onChildAdded: Name: Ali
D/MyTag: onChildAdded: Age: 50
D/MyTag: This is Person: Person{Name='Ali', Age=50}
D/MyTag: onChildAdded: Name: New Name
D/MyTag: onChildAdded: Age: 80
D/MyTag: This is Person: Person{Name='New Name', Age=80}

回调方法是针对每个字段分别使用的,它一次返回一个字段,但这可以使setField被调用两次,因此这就是为什么要调用它的原因。但是对于儿童听众,情况更糟。

这是具有childListener实现的读取代码方法。

private void readData(View view) {
        //read data here
        mRef.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
                Map<String,Object> data= (Map<String, Object>) dataSnapshot.getValue();
                Person p=dataSnapshot.getValue(Person.class);

                Log.d(TAG, "onChildAdded: Name: "+data.get("Name"));
                Log.d(TAG, "onChildAdded: Age: "+data.get("Age"));

                Log.d(TAG, "onChildAdded: Person is: "+p);
            }
            @Override
            public void onChildChanged(@NonNull DataSnapshot       dataSnapshot, @Nullable String s) {
            }
            @Override
            public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
            }
            @Override
            public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
            }
            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {

            }
        });
}

这是我第一次单击读取数据按钮时的logcat输出:

D/MyTag: onChildAdded: Name: Ali
D/MyTag: onChildAdded: Age: 50
D/MyTag: onChildAdded: Person is: Person{Name='Ali', Age=50}
D/MyTag: onChildAdded: Name: New Name
D/MyTag: onChildAdded: Age: 80
D/MyTag: onChildAdded: Person is: Person{Name='New Name', Age=80}

这是添加名称为“ Usman”且年龄为50岁的新孩子的输出

D/MyTag: onChildAdded: Name: Usman
D/MyTag: onChildAdded: Age: null
D/MyTag: onChildAdded: Person is: Person{Name='Usman', Age=0}

这仅发生在回调中。年龄没有返回,但值已正确存储在数据库中。

请帮助。我仅连接了一个侦听器,并且在停止运行进程后始终运行该应用程序。 谢谢。

1 个答案:

答案 0 :(得分:0)

运行此代码时:

mRef.child(key).child("Name").setValue(name);
mRef.child(key).child("Age").setValue(age);

setValue的每次调用都会分别发送到Firebase数据库。因此,您要对数据库执行两个独立的写操作。每个写操作都可以发送给侦听器。

要使它们作为一个写操作退出,请将这两个属性组合为一个对setValue()的调用:

Map<String, Object> values = new HashMap<String, Object>();
map.put("Name", name);
map.put("Age", age);
mRef.child(key).setValue(values);

这将对数据库执行一次写入,最终结果相同。但这只会触发一次侦听器。

更新

如果我们回头看您的初始代码:

mRef.child(key).child("Name").setValue(name);
mRef.child(key).child("Age").setValue(age);

第一次调用setValue将导致创建$key,而第二次调用仅更新其下的Age节点。在mRef上使用子侦听器时,这意味着对setValue()的第一次调用会触发onChildAdded,而第二个调用会触发onChildChanged()