2016-10-09 11 views
1

現在、私はFirebaseデータベースを使用しているAndroid Appをビルドしています。Android - Firebase - getChildrenCount()メソッドの問題

私のコードでは、Firebaseデータベースの特定のパスにある子供の数を取得しようとしています。 Firebase固有のパスの参照を定義したら、DataSnapshotを取得してgetChildrenCount()を呼び出すためにリスナーを作成しました。

その後、この結果をFOR-Loopで使用したいと考えています。ただし、コードは機能しません。

Log.v("NUM_OF_PRODUCTS",numOfProducts+"")が0の場合、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

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"); 

これを実行すると、出力は次のようになります。

リスナー

を取り付ける前に、

リスナーを取り付けた後

InDataChangeで

これはおそらく、期待した順序ではありません。しかし、ループを開始したときにnumOfProductsがまだ0である理由は完全に説明されています。データはFirebaseからまだロードされていません。

ほとんどすべての開発者からの最初の反応では、「私はこれを望んでいません。正しい順序で実行するにはどうすればよいですか?」これは当然の反応ですが、Web /クラウドAPIを使ってプログラミングするときは、どこにでも入るためには抑制する必要があります。 Setting Singleton property value in Firebase Listener

解決策は、「最初に製品の数を取得してから、製品をループする」から「製品を入手するたびに、 "それらの上にループします"。あるコードで

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番目のリスナーは必要ありません。ノードを取得すると、そのノードの下のの下にすでにのすべてのデータが取得されているためです。子ノードをループすることができます。
  2. これは最初にHashMapを抽出するのではなく、子スナップショットから直接データを読み込むだけです。

もう1つの変更点は、製品名の別のリストを保持することです。すべての製品データを検索して名前のリストを表示すると、無駄です。単に製品名のリストを別に保管しておけば、代わりにその商品名をロードすることができます。 Firebase(またはほぼすべてのNoSQLデータベース)を使用する際の共通テーマは、データベースのデータを画面に表示する方法でモデル化することです。

関連する問題