1

During the execution of the method, you need to get 2 sets of data from the same branch. Then adjust the values and write back. How to organize this method correctly? I did it by nesting listeners. Is there a more correct approach? And how do I get this method to return a result (for example boolean) when the Firebase write command succeeds?

Method Code:

public static void UpdateBalancesInFB (final long DateMove, final double DeltaValue, DatabaseReference FBRef){
    //TODO: как получить успешное выполнение метода?!
    final DatabaseReference mDBref = FBRef.child(MyTypesAndUtils.sFB_Balances);
    final boolean result = false;

    /*получаем с Firebase выгрузку последней записи и данных следующих за Date_min */
    final Query mQuery = mDBref.orderByKey();
    mQuery.limitToLast(1).addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {
            final long[] Date_last = {DateMove};
            final double[] Value_last = {DeltaValue};

            for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                Date_last[0] = Long.valueOf(snapshot.getKey());
                Value_last[0] = snapshot.getValue(Double.class);
            }
            mQuery.startAt(String.valueOf(DateMove)).addListenerForSingleValueEvent(new ValueEventListener() {
                @Override
                public void onDataChange(DataSnapshot dataSnapshot) {
                    boolean mark = false;
                    Map<String, Object> mQueryBalances = new HashMap<>();
                    for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
                        mQueryBalances.put(snapshot.getKey(), snapshot.getValue());
                        mark = true;
                    }
                    /*корректируем значения остатков после даты движения*/
                    for (HashMap.Entry<String, Object> entry : mQueryBalances.entrySet()) {
                        Date_last[0] = Long.valueOf(entry.getKey());
                        Value_last[0] = Double.valueOf(entry.getValue().toString()) + DeltaValue;
                        mQueryBalances.put(entry.getKey(), Value_last[0]);
                    }

                    /*дополняем коллекцию недостающими элементами до даты движения и 1 элемент после нее*/
                    //это возможно только если mQueryBalances пустой
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTimeInMillis(Date_last[0]);         //последняя запись остатков
                    calendar.set(Calendar.DAY_OF_MONTH, 1);         //переводим на 1й день месяца (на всякий случай)

                    while (!mark) {                                 // это возможно только если mQueryBalances пустой
                        calendar.add(Calendar.MONTH, 1);
                        Date_last[0] = calendar.getTimeInMillis();
                        if (Date_last[0] <= DateMove)
                            mQueryBalances.put(String.valueOf(Date_last[0]), Value_last[0]);
                        else{
                            mQueryBalances.put(String.valueOf(Date_last[0]), Value_last[0]+DeltaValue);
                            mark = true;
                        }
                    }

                    /*Записываем в Firebase*/
                    mDBref.updateChildren(mQueryBalances, new DatabaseReference.CompletionListener() {
                        @Override
                        public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
                            //result = true;
                        }
                    });
                }

The call code of this method is also executed inside the listener:

myRef.child(MyTypesAndUtils.sFB_Movements).child(mMove_output.getKeyFB()).setValue(mMove_output).addOnSuccessListener(this, new OnSuccessListener<Void>() {
        @Override
        public void onSuccess(Void aVoid) {
            long Date2, Date1;
            double Value2, Value1;

            Date2 = mMove_output.getDateInMilisec();
            Value2 = mMove_output.getValue()*mMove_output.getKind();
            if (mMove_input != null) {
                Date1 = mMove_input.getDateInMilisec();
                Value1 = mMove_input.getValue()*mMove_input.getKind();
                if (Date2==Date1)
                    MyTypesAndUtils.UpdateBalancesInFB(Date2, (Value2-Value1), myRef);
                else {
                    MyTypesAndUtils.UpdateBalancesInFB(Date1, (-Value1), myRef);
                    MyTypesAndUtils.UpdateBalancesInFB(Date2, Value2, myRef);
                }
            }
            else    MyTypesAndUtils.UpdateBalancesInFB(Date2, (Value2), myRef);

            Toast.makeText(getApplicationContext(), "Сохранение произошло", Toast.LENGTH_SHORT).show(); //TODO: переписать в string
            finish();
        }
    });
ReyAnthonyRenacia
  • 17,219
  • 5
  • 37
  • 56
  • There is nothing wrong using nested listeners, but the code usually gets quite messy and full of callbacks. I suggest you to check some Reactive model for your code to make it easier to read and more clean. It will save you hours of works and lots of headaches. There is a Firebase RX wrapper that you could use and could simplify your logic, but if you don't know the reactive pattern will be a bit hard to understand. Good luck! https://github.com/FrangSierra/RxFirebase – Francisco Durdin Garcia Jun 04 '18 at 13:04

2 Answers2

0

There is nothing wrong about nested listeners as long as you remove them according to the life-cycle of your activity. So your code looks correct. There is a way in which you can achieve the same thing using custom callbacks but there is no need to use them rather then this straightforward example of nested listeners.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
0

I decided to transfer my logic from the listeners to the server part, since there is no user participation in it, and the client is not needed I used Firebase function in JavaScript ...