Проблема с Firebase onChildRemoved () и removeValue (), работающими на чувствительной ко времени операции - PullRequest
0 голосов
/ 24 июня 2018

У меня есть приложение, которое предлагает некоторые предложения для некоторых вещей.Идея заключается в том, что предложение добавлено мной в другое созданное мной приложение, а затем будет отображаться на всех пользовательских устройствах одновременно.

Мне нужен только один пользователь, чтобы иметь возможность принять предложение, когда первый пользовательклики я вызываю removeValue () на предложение ref.Предложение корректно удалено из базы данных и из списка других пользователей.

Проблема в том, что когда 2 клика происходят одновременно, предложение удаляется, но onChildRemoved () не имеет времени для вызова, поэтому оба пользователя теперь имеют одинаковое предложение!

Есть ли какая-нибудь другая идея, как сделать эту операцию более точной и учитывающей время?

ОБНОВЛЕНИЕ , как предложено в svi.data, я попробовал этот фрагмент кода по клику пользователяно все же проблема возникает.

    offerUnAnsweredRef.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

            boolean stillThere = false;

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

                if (offerSnap.getKey().equals(requestedOffer.getCurrentNodeKey())) {
                    stillThere = true;
                }
            }

            if (stillThere) {
                Timber.d("We have it " + requestedOffer.getEmployeeKey());
                Toast.makeText(getContext(), "Welcome Dear ", Toast.LENGTH_SHORT).show();
                offerUnAnsweredRef.child(requestedOffer.getCurrentNodeKey()).removeValue();
            } else {
                Toast.makeText(getContext(), "Go Away Bear", Toast.LENGTH_SHORT).show();

            }
        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });

ОБНОВЛЕНИЕ 2

На самом деле решение построено поверх ответа svi.data с некоторыми изменениями, поэтому я хотел поделитьсярабочий код, чтобы помочь, если кто-либо когда-либо сталкивается с подобной ситуацией

offerUnAnsweredRef.child(requestedOffer.getCurrentNodeKey()).runTransaction(new Transaction.Handler() {
        @NonNull
        @Override
        public Transaction.Result doTransaction(@NonNull MutableData mutableData) {


            RequestedOffer o = mutableData.getValue(RequestedOffer.class);
            if (o == null) {
                return Transaction.abort();
            }

            if (o.getEmployeeKey() == null) {
                o.setEmployeeKey(employee.getUid());
                mutableData.setValue(o);
                return Transaction.success(mutableData);

            } else {
                return Transaction.success(mutableData);
            }
        }

        @Override
        public void onComplete(DatabaseError databaseError, boolean b,
                               DataSnapshot dataSnapshot) {
            // check if the transaction completed successfully
            // or if it failed
            RequestedOffer o = dataSnapshot.getValue(RequestedOffer.class);
            if (o.getEmployeeKey() == employee.getUid()) {
                getActivity().runOnUiThread(() -> Toast.makeText(getActivity(), "Hello", Toast.LENGTH_SHORT).show());
                DatabaseReference databaseReference = FirebaseFactory.getDatabase()


            } else {
                getActivity().runOnUiThread(() -> Toast.makeText(getActivity(), "NO", Toast.LENGTH_SHORT).show());
            }
        }

, потому что, как сказано в документации,

public abstract Transaction.Result doTransaction (MutableData currentData)

Этот метод будет вызываться, возможно, несколько раз, с текущими данными в этом месте.Он отвечает за проверку этих данных и возвращает Transaction.Result, указывающий либо требуемые новые данные в местоположении, либо что транзакция должна быть прервана.

Поэтому я добавил код для проверки onComplete, чтобы убедиться, чточто он позвонил только один раз.

1 Ответ

0 голосов
/ 24 июня 2018

Из того, что я понимаю:

1) У вас есть специальное приложение для добавления предложений (вами).

2) У вас есть другое приложение для чтенияпредложения (по пользователям).

3) Если это так, то оба приложения используют один и тот же проект.

4) Когда пользователь нажимает предложение, он / она получает предложение, затем вы удалите предложение из базы данных.

5) Теперь, когда 2 пользователя щелкают одно и то же предложение, у него нет времени на удаление предложения из списка другого пользователя, поэтому они в итоге получают одно и то же.предложение.

Теперь кажется, что вы не хотите, чтобы пользователи получали одинаковые предложения, и проблема действительно заключается в сроках.

Возможное решение:

1) Когда пользователь щелкает предложение, вы запускаете ValueEventListener () для узла offers в базе данных и проверяете, существует ли предложение.

2) Если предложение существует, дайте ему /ей предложение и удалите его.

3) Теперь, когда 2 пользователя нажимают одно и то же предложение, ValueEventListener, который я используюked about предоставит вам некоторое время, прежде чем реагировать.

4) Таким образом, пользователи не должны получать одинаковые предложения.

Надеюсь, что это решит вашу проблему.

ОБНОВЛЕНИЕ:

Поскольку это условие гонки между пользователями, самое время поговорить о транзакциях.Firebase предоставляет хороший способ для непосредственного чтения и записи одновременных обновлений на один и тот же узел (как в вашем случае).

Я хочу, чтобы ваша база данных была такой:

Offers
|
|------offer_id_1
       |
       |-----taken:false
       |-----......
       |-----......
|
|-------offer_id_2
       |
       |------taken:false
       |------......
       |------......

Позвольте мне объяснитьВ приведенной выше структуре каждое предложение, которое вы публикуете в другом приложении, по умолчанию будет иметь флаг taken, и по умолчанию оно должно иметь значение false.

Теперь, как вы видите выше offer_id_1 иoffer_id_2 - это push-идентификатор или случайный идентификатор, указанный для предложения (когда пользователь нажимает на предложение, вы должны получить ссылку на этот ключ .... Я полагаю, вы знаете, как это сделать).

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

public class Offer{

public boolean taken;
......
......


}

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

public void RunTransactionFor(String offer_id){


//first refer to your offers
DatabaseReference offers_ref = FirebaseDatabase.getInstance().getReference().child("offers").child(offer_id);

//run a transaction (a transaction is fast it reads and writes directly)

offer_ref.runTransaction(new Transaction.Handler() {
        @Override
        public Transaction.Result doTransaction(MutableData mutableData) {
            //this is a ref to the offer class 
            Offer offer = mutableData.getValue(Offer.class);

            if (offer == null) {
                return Transaction.success(mutableData);
            }
            if(offer.taken == false){
             //take the offer
             offer.taken = true;
             //show a message
             Toast.makeText(context, "you took the offer",...).show();
             //now you can remove the offer
             offers_ref.setValue(null);//or delete it your way 



            }else{
             //too late the offer is taken
             Toast.makeText(context, "too late the offer is gone",...).show(); 
             //do nothing  
            } 

            // Set value and report transaction success
            mutableData.setValue(offer);
            return Transaction.success(mutableData);
        }

        @Override
        public void onComplete(DatabaseError databaseError, boolean b,
                               DataSnapshot dataSnapshot) {
            // Transaction completed 

        }
    });

}

Теперь, когда пользователь щелкает предложение в списке, сохраните идентификатор предложения и передайте его вышеуказанной функции следующим образом:

//after user clicks

String offer_id = .......

//run transaction

RunTransactionFor(offer_id);

Примечание: транзакции работают толькоКроме того, они не могут работать в автономном режиме.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...