我一直在查看firebase文档,并尝试使用我正在构建的Android应用程序来运行firebase。
我能找到的所有代码示例都显示了如何获取值,然后立即在firebase ValueEventListener内部类中打印它。我想获取值并将它们存储在一个数组中供以后使用。
我尝试做的是当用户点击高分按钮时,客户端从firebase获取当前记分板,并将该列表发送到高分路板类。
更新:我现在能够检索数据但是有一种行为让我感到非常困惑。如果用户单击高分数按钮,则会填充分数列表,高分类正确显示10个元素。如果我退出记分板并再次输入,它现在将显示所有分数两次(可以理解的是,因为我只是添加到相同的列表)。但任何对scores.clear()或score = new ArrayList()的调用;即使在第二次点击之后,两次点击之间的分数也是空的(我假设填充分数,然后清空它并重新填充它会在那里留下10个项目)这种行为是我认为我的分数数组在第一次发布时从未填充的原因因为我在load函数内部有一个得分,因为我不想复制值。如果有人能够解释为什么会发生这种情况会非常棒。
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener {
ArrayList<Score> scores;
Firebase myFirebase;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Firebase.setAndroidContext(this);
myFirebase = new Firebase(FirebaseURL);
scores = new ArrayList<Score>();
loadScoresFromFireBase();
}
public void loadScoresFromFireBase() {
String entry = "";
for (int i = 0; i < 10; i++) {
entry = "Name_" + i;
myFirebase.child("users").child(entry).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
//getValue correctly returns a Score object
scores.add(snapshot.getValue(Score.class));
}
@Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//....
if ( id == 2) {
Intent intent = new Intent(getApplicationContext(), ScoreBoard.class);
Bundle bundle = new Bundle();
//calling scores.clear() here leaves the scores array empty even though loadscores is called afterwards.
loadScoresFromFireBase();
Collections.sort(scores);
bundle.putSerializable("players", scores);
intent.putExtra("players", bundle);
startActivityForResult(intent, 0);
}
}
}
答案 0 :(得分:4)
你正在为经典的异步陷阱而堕落:
loadScoresFromFireBase();
Collections.sort(scores);
当您拨打loadScoresFromFireBase()
(拼写为Firebase
btw)时,程序启动同步服务器的分数。加载此数据需要一些时间。 Firebase不会让您的程序等待(这将是一个糟糕的用户体验),而是让您传入ValueEventListener
,然后在数据可用时调用它。但是,在加载(第一个)数据之前,你会立即调用Collections.sort(scores)
。
您可以通过添加一些日志语句轻松地看到这一点:
public void loadScoresFromFireBase() {
String entry = "";
for (int i = 0; i < 10; i++) {
entry = "Name_" + i;
myFirebase.child("users").child(entry).addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot snapshot) {
System.out.println("Adding score to array");
scores.add(snapshot.getValue(Score.class));
}
public void onCancelled(FirebaseError firebaseError) { }
});
}
}
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//....
if (id == 2) {
Intent intent = new Intent(getApplicationContext(), ScoreBoard.class);
Bundle bundle = new Bundle();
System.out.println("Before calling loadScores");
loadScoresFromFireBase();
System.out.println("After calling loadScores");
Collections.sort(scores);
System.out.println("After sorting scores");
这些日志语句的输出将是:
在调用loadScores之前
调用loadScores后
排序后得分
为数组添加分数
这可能不是你所期望的。但是当你处理异步/事件驱动代码时,这是完全正常的,就像现代互联网编程中常见的一样。
最直接的解决方案是将需要分数的代码移动到加载它们的函数中:
public void loadScoresFromFireBase() {
String entry = "";
for (int i = 0; i < 10; i++) {
entry = "Name_" + i;
myFirebase.child("users").child(entry).addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot snapshot) {
scores.add(snapshot.getValue(Score.class));
Collections.sort(scores);
Intent intent = new Intent(getApplicationContext(), ScoreBoard.class);
Bundle bundle = new Bundle();
bundle.putSerializable("players", scores);
intent.putExtra("players", bundle);
startActivityForResult(intent, 0);
}
public void onCancelled(FirebaseError firebaseError) { }
});
}
}
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//....
if (id == 2) {
loadScoresFromFireBase();
另见我刚才写的这个答案:Setting Singleton property value in Firebase Listener
以及关于同一问题的这些伟大答案:
虽然这些是关于JavaScript的,但它们处理完全相同的问题。
答案 1 :(得分:0)
由于您尚未初始化scores
,因此在调用add
时会收到NullPointerException
如果你这样做
ArrayList<Score> scores = new ArrayList ();
它应该有用