Firebase returning keys of child node in different

2020-02-06 16:32发布

问题:

I am getting a snapshot of the data from my Firebase database to retrieve a users list, but the order of the keys being returned is different depending on the Android version/ device being used.

For demonstrative purposes I have shortened the method, but it is essentially as follows:

public void getUsers(){
    Firebase ref = new Firebase("https://myFirebaseID.firebaseio.com");
    final Firebase userRef = ref.child("users");

    userRef.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot snapshot) {
           snapshot.toString();
        }                                                           
    });
}

It is the data I get from calling toString() on the snapshot object (snapshot.toString()) that changes order.

I have tried it on 4 devices. The 2 running Lollipop (Nexus 7 5.1.1 & Galaxy s4 5.01) return the data in the same order. And the 2 two other devices (HTC Sensation 4.0.3 and Motorola G2 4.4.4) return the data in the same order (but a different order to devices with Lollipop).

There is no difference in the code used, and the data in the database was completely unchanged at the times when I retrieved the snapshots.

Here is the data order on the 4.4.4 and 4.0.3 devices:

DataSnapshot { 
  key = users, 
  value = {
    114585619420240714499={
      **userIDOfCUser**=114585619420240714499,
      **NameOfCUser**=testName,
      **EmailOfCUser**=testerfireapp@gmail.com,
      **friends**={
        103902248954972338254={
          **userIDOfFriend**=103902248954972338254, 
          **NameOfFriend**=testName2 
        }
      }
    }  

Here is the data order on the 5.1.1 and 5.01 devices:

DataSnapshot { 
  key = users, 
  value = {
    114585619420240714499={
      **NameOfCUser**=testName, 
      **userIDOfCUser**=114585619420240714499, 
      **friends**={
        103902248954972338254={
          **NameOfFriend**=testName2 ,
          **userIDOfFriend**=103902248954972338254
        }
      }, 
      **EmailOfCUser**= testerfireapp@gmail.com
    }
  }
}

Why is the data being delivered in different orders depending on the android version/device being used? is there another difference I am unaware of?

Edit: When iterating through the snapshot as follows, the different ordering of the keys still persists accross different versions of Android:

public void getUsers2(){

  Firebase ref = new Firebase("https://myFirebaseID.firebaseio.com");
  final Firebase userRef = ref.child("users");
  userRef.addListenerForSingleValueEvent(new ValueEventListener() {

    @Override
    public void onDataChange(DataSnapshot snapshot) {
        for (DataSnapshot keys : snapshot.getChildren()) {
            //String to temporarily store the whole child of the inidividual user's DB node ----> It still produces different order of the keys 
            String tempKey = keys.getValue().toString();

            //The problem persists if I code it like this as well. 
            String tempKey2 = snapshot.child(keys.getKey()).getValue().toString();

        }
    }
  });
}

回答1:

The documentation for Firebase's REST API contains this warning:

When using the REST API, the filtered results are returned in an undefined order since JSON interpreters don't enforce any ordering. If the order of your data is important you should sort the results in your application after they are returned from Firebase.

In other words: the properties in a JSON object can be in any order. A JSON parser is free to re-arrange them as it sees fit.

You are not using the REST API, but the Firebase Android SDK. All the Firebase SDKs take care if hiding such re-ordering from you or (when hiding it is not possible) telling you explicitly what the order is (e.g. the previousChildName argument to the onChildAdded callback)

But from the data you're showing it seems like you're parsing the output from dataSnapshot.toString(). While this is totally valid, it does mean that you're choosing to handle the ordering yourself. In general it's best to stick to using the methods of the Firebase Android SDK, since they handle things like ordering for you:

public void onDataChange(DataSnapshot snapshot) {
  for (DataSnapshot friendSnapshot: snapshot.getChildren()) {
    System.out.println(friendSnapshot.child("NameOfFriend").getValue());
  }
} 

Update

From your update it seems like you have a query on users and then want to also loop over their friends. With the Firebase Android API you'd do that with:

Firebase ref = new Firebase("https://myFirebaseID.firebaseio.com");
final Firebase userRef = ref.child("users");
userRef.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot userSnapshot) {
    DataSnapshot friendsSnapshot = userSnapshot.child("friends");
    for (DataSnapshot friendSnapshot : friendsSnapshot.getChildren()) {
      System.out.println(friendSnapshot.child("NameOfFriend").getValue());
    }
  }
});