目前,我正在构建使用Firebase数据库的Android应用。
在我的代码中,我尝试在Firebase数据库的特定路径中获取子项数。定义了Firebase特定路径的引用后,我创建了一个Listener以获取DataSnapshot,然后调用getChildrenCount()。
之后,我想在FOR-Loop中使用此结果。但是,代码不起作用。
似乎首先执行FOR-Loop(我意识到因为Log.v("NUM_OF_PRODUCTS",numOfProducts+"")
输出0输入3)并且之后执行Listener,而在代码中,侦听器代码首先出现并且FOR-Loop代码接下来。
我的部分代码:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_product_selection);
selected_cat = getIntent().getExtras().getString("pass_selected_cat");
listView = (ListView) findViewById(R.id.listView);
final ArrayAdapter<String> adapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, arrayList);
listView.setAdapter(adapter);
fbdatabase = FirebaseDatabase.getInstance();
fbref_products = fbdatabase.getReference("PRODUCTS/SM_0/" + selected_cat);
//Listener for getting numOfProducts.
fbref_products.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
numOfProducts = (int) dataSnapshot.getChildrenCount();
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
//Print numOfProducts before FOR-Loop being executed.
Log.v("NUM_OF_PRODUCTS",numOfProducts+"");
//FOR-Loop
for (int i = 0; i < numOfProducts; i++) {
String str = i + "";
DatabaseReference product_ref = fbref_products.child(str);
product_ref.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
Map<String, Object> map = (Map<String, Object>) dataSnapshot.getValue();
String productName = (String) map.get("productName");
arrayList.add(productName);
adapter.notifyDataSetChanged();
}
@Override
public void onCancelled(DatabaseError databaseError) {
//textView.setText("The read failed: " + databaseError.getMessage());
}
});
}
我的代码工作正常,如果改为numOfProducts
,我们有一个特定的数字。
任何人都可以帮助我吗?
答案 0 :(得分:1)
Firebase数据库中的数据是异步加载的。当您运行for
循环时,它尚未加载。
如果在代码中放置一些日志语句,最容易看到这一点:
System.out.println("Before attaching listener");
fbref_products.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println("In onDataChange");
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
System.out.println("After attaching listener");
当你运行它时,输出将是:
在附加监听器之前
附加监听器后
在onDataChange
这可能不是您预期的顺序。但它完全解释了当你开始循环时numOfProducts
仍为0的原因:数据尚未从Firebase加载。
几乎每个开发人员的初步反应都是&#34;我不想要这个。如何让它以正确的顺序执行?&#34;这是一种自然的反应,但是当您针对Web /云API进行编程时,您必须要压制它。我还建议在这里阅读我的答案:Setting Singleton property value in Firebase Listener
解决方案是重新构建您的解决方案,首先我将获得产品数量,然后我将循环使用产品&#34;到了#34;每当我拿到产品时,我都会循环使用它们。#/ p>
在以下代码中:
fbref_products.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
numOfProducts = (int) dataSnapshot.getChildrenCount();
for (int i = 0; i < numOfProducts; i++) {
String str = i + "";
DatabaseReference product_ref = fbref_products.child(str);
product_ref.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
Map<String, Object> map = (Map<String, Object>) dataSnapshot.getValue();
String productName = (String) map.get("productName");
arrayList.add(productName);
adapter.notifyDataSetChanged();
}
@Override
public void onCancelled(DatabaseError databaseError) {
//textView.setText("The read failed: " + databaseError.getMessage());
}
});
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
除了处理异步事件之外,您还可以大大简化此代码:
fbref_products.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot productSnapshot: dataSnapshot.getChildren()) {
String productName = productSnapshot.child("productName").getValue(String.class);
arrayList.add(productName);
adapter.notifyDataSetChanged();
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
的变化:
您应该考虑的另一个更改是保留单独的产品名称列表。您现在检索所有产品数据以显示名称列表,这是浪费。如果您只保留产品名称的单独列表,则可以加载该名称。使用Firebase(或几乎任何NoSQL数据库)时,您会发现一个共同的主题:按照您在屏幕上显示的方式为数据库中的数据建模。