Android + Firebase: синхронная для асинхронной функции

Я делаю функцию (Java - Android), которая должна вернуть мне список объектов, заполненных данными из Firebase. Моя проблема в том, что мне нужно отправить результат слушателю сразу после того, как я переберу все элементы в for, взгляните на код:

final DatabaseReference beaconMessageRef = dataSnapshot.getRef().getRoot().child("region_messages/" + regionKey);
beaconMessageRef.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {

        if(!dataSnapshot.exists())
            FirebaseHelper.this.messageByBeaconListener.onSuccess(null);

        for (DataSnapshot regionMessageDS : dataSnapshot.getChildren()) {

            RegionMessage regionMessage = regionMessageDS.getValue(RegionMessage.class);
            String msgKey = regionMessageDS.getKey();

            final DatabaseReference messageRef = regionMessageDS.getRef().getRoot().child("messages/"+msgKey);
            messageRef.addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {

                    if(dataSnapshot.exists()){
                        Message msg = dataSnapshot.getValue(Message.class);
                        FirebaseHelper.this.addMessageByBeacon(msg);
                    }
                }

                @Override
                public void onCancelled(DatabaseError databaseError) {
                    FirebaseHelper.this.messageByBeaconListener.onFail(databaseError);
                }
            });
        }

        List<Message> msgs = FirebaseHelper.this.beaconMessageList;
        FirebaseHelper.this.beaconMessageList = null;
        FirebaseHelper.this.messageByBeaconListener.onSuccess(msgs);
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        FirebaseHelper.this.messageByBeaconListener.onFail(databaseError);
    }
});

Мне нужно выполнить эту строку: FirebaseHelper.this.messageByBeaconListener.onSuccess(msgs); сразу после того, как я переберу все элементы в for.

Как я могу этого добиться?


person André Luiz    schedule 09.06.2017    source источник


Ответы (1)


Что-то, что является асинхронным, не может быть надежно синхронизировано на Android. Всякий раз, когда вы чувствуете желание сделать это, сделайте глубокий вдох и повторите это первое предложение. Или прочитайте мой ответ здесь: Установка значения свойства Singleton в Firebase Listener

Вместо этого вам придется делать то, что делают все: переместить код, которому нужны данные, в функцию обратного вызова, которая срабатывает, когда данные доступны.

В вашем случае это немного сложнее, так как вы загружаете несколько элементов. К счастью, вы знаете, сколько предметов вам нужно загрузить. Поэтому, если вы введете счетчик для отслеживания количества загруженных элементов, вы будете знать, когда закончите:

final DatabaseReference beaconMessageRef = dataSnapshot.getRef().getRoot().child("region_messages/" + regionKey);
beaconMessageRef.addListenerForSingleValueEvent(new ValueEventListener() {
  @Override
  public void onDataChange(DataSnapshot dataSnapshot) {
    if(!dataSnapshot.exists())
      FirebaseHelper.this.messageByBeaconListener.onSuccess(null);

    for (DataSnapshot regionMessageDS : dataSnapshot.getChildren()) {

      RegionMessage regionMessage = regionMessageDS.getValue(RegionMessage.class);
      String msgKey = regionMessageDS.getKey();

      final Integer[] counter = new Integer[1];
      final DatabaseReference messageRef = regionMessageDS.getRef().getRoot().child("messages/"+msgKey);
      messageRef.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot innerSnapshot) {
          if(innerSnapshot.exists()){
            Message msg = innerSnapshot.getValue(Message.class);
            FirebaseHelper.this.addMessageByBeacon(msg);
          }
          counter[0] = new Integer(counter[0].intValue()+1);
          if (counter[0].equalTo(dataSnapshot.numChildren()) {
            List<Message> msgs = FirebaseHelper.this.beaconMessageList;
            FirebaseHelper.this.beaconMessageList = null;
            FirebaseHelper.this.messageByBeaconListener.onSuccess(msgs);
          }
        }

        @Override
        public void onCancelled(DatabaseError databaseError) {
          FirebaseHelper.this.messageByBeaconListener.onFail(databaseError);
        }
      });
    }
  }

  @Override
  public void onCancelled(DatabaseError databaseError) {
    FirebaseHelper.this.messageByBeaconListener.onFail(databaseError);
  }
});

См. этот ответ для объяснения одноэлементного массива: https://stackoverflow.com/a/5977866/209103

person Frank van Puffelen    schedule 09.06.2017
comment
Спасибо за ваш ответ, он решил проблему. Я часто сталкиваюсь с этим сценарием при работе с NodeJS и решаю его с помощью промисов. Я изучал Future, чтобы проверить, смогу ли я сделать то же самое. Но мне нравится ваше решение, довольно легкое и простое. - person André Luiz; 09.06.2017
comment
привет @frank, теперь у меня такая же проблема, я пытаюсь вызвать функцию firebase внутри цикла for. помогите мне, я не могу понять код выше!. - person Kuldeep mourya; 25.04.2018