Android - Firebase - getChildrenCount()方法问题

时间:2016-10-09 08:27:57

标签: android firebase firebase-realtime-database

目前,我正在构建使用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,我们有一个特定的数字。

任何人都可以帮助我吗?

1 个答案:

答案 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) {
    }
});

的变化:

  1. 这只使用一个监听器。不需要您的第二个侦听器,因为检索节点也已经检索该节点下的所有数据。您可以遍历子节点。
  2. 这并不首先提取HashMap,而只是直接从子快照中读取数据。
  3. 您应该考虑的另一个更改是保留单独的产品名称列表。您现在检索所有产品数据以显示名称列表,这是浪费。如果您只保留产品名称的单独列表,则可以加载该名称。使用Firebase(或几乎任何NoSQL数据库)时,您会发现一个共同的主题:按照您在屏幕上显示的方式为数据库中的数据建模。