0

I am trying to update the value of daysOnWhichPresent field by 1 of Firebase database. Here I have read the value two times before and after updating the value of daysOnWhichPresent but it is not correctly reading from the database. In first read the value should be 6 but after fetching the data its value is 0.

Also this code is not letting me update the value of daysOnWhichPresent, the permission is set to read/write to all. Please help me.

Here is the Firebase Database Tree

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

public class AttendanceActivity extends AppCompatActivity {

    FirebaseDatabase firebaseDatabase;
    DatabaseReference databaseReference;
    String uniqueVolunteerId;
    long daysPresent;
    TextView textAttendance;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_attendance);

        Bundle bundle = getIntent().getExtras();
        uniqueVolunteerId = bundle.getString("uniqueVolunteerId");

        Log.i("TAG", uniqueVolunteerId);

        firebaseDatabase = FirebaseDatabase.getInstance();
        databaseReference = firebaseDatabase.getReference("volunteers").child(uniqueVolunteerId);

        textAttendance = findViewById(R.id.text_attendance);


        databaseReference.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                daysPresent = dataSnapshot.child("daysOnWhichPresent").getValue(Long.class);
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.i("TAG", "Error in data fetching from server 1");

            }
        });

        Log.i("TAG", Long.toString(daysPresent));

        databaseReference.child("daysOnWhichPresent").setValue(daysPresent+1);


        databaseReference.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                daysPresent = dataSnapshot.child("daysOnWhichPresent").getValue(Long.class);
                Log.i("TAG", Long.toString(daysPresent));
                textAttendance.setText(Long.toString(daysPresent));
                Toast.makeText(getApplicationContext(), Long.toString(daysPresent), Toast.LENGTH_LONG).show();
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.i("TAG", "Error in data fetching from server 1");

            }
        });


    }
}

The log shows that the value of daysPresent is 0, 1 and 6 But it should be 6, 7 and 7. Please help me.

As suggested by Alex Mamo, I have changed the code as follows...

package com.aayush.reboot.nssnitdurgapur;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.MutableData;
import com.google.firebase.database.Transaction;
import com.google.firebase.database.ValueEventListener;

import java.util.HashMap;

public class AttendanceActivity extends AppCompatActivity {


    String uniqueVolunteerId;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_attendance);

        Bundle bundle = getIntent().getExtras();
        uniqueVolunteerId = bundle.getString("uniqueVolunteerId");

        incrementAttendanceByOne(uniqueVolunteerId);


        DatabaseReference volunteerRef = FirebaseDatabase.getInstance().getReference("volunteers").child(uniqueVolunteerId).child("daysOnWhichPresent");

        volunteerRef.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                Long daysPresent = dataSnapshot.getValue(Long.class);
                Log.i("TAG", "Value of daysOnWhichPresent in onCreate method "+ Long.toString(daysPresent));
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }
        });
    }

    private void incrementAttendanceByOne(String uniqueVolunteerId) {

        Log.i("TAG", "Unique ID: "+uniqueVolunteerId);
        DatabaseReference volunteerRef = FirebaseDatabase.getInstance().getReference("volunteers").child(uniqueVolunteerId);
        DatabaseReference daysPresentRef = volunteerRef.child("daysOnWhichPresent");

        daysPresentRef.runTransaction(new Transaction.Handler() {
            @Override
            public Transaction.Result doTransaction(MutableData mutableData) {
                Long daysPresent = mutableData.getValue(Long.class);

                if (daysPresent == null) {
                    Log.i("TAG", "Days Present is null---"+Long.toString(daysPresent));
                    return Transaction.success(mutableData);
                }
                Log.i("TAG", "Old value in function incrementAttendanceByOne "+Long.toString(daysPresent));

                mutableData.setValue(daysPresent+1);
                daysPresent = mutableData.getValue(Long.class);
                Log.i("TAG", "Updated value in function incrementAttendanceByOne "+Long.toString(daysPresent));

                return Transaction.success(mutableData);
            }

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

            }
        });
    }
}

I have printed value of daysOnWhichPresent before and after the execution of transaction which shows the correct result.

But when i print the value in 'onCreate()' method after execution of incrementAttendanceByOne() a single log statement is giving me two values first updated one and later the old value, which tells that the data has been changed in database for a moment and again it reverted back to its previous value.

What should be the problem and how to solve this?

Here is the Logcat

05-22 10:51:44.000 22737-22737/com.aayush.reboot.nssnitdurgapur I/TAG: Unique ID: G8ho4gth5ZPU7z8Sk9uK40QbGtq1
05-22 10:51:44.010 22737-22811/com.aayush.reboot.nssnitdurgapur I/TAG: Old value in function incrementAttendanceByOne 25 Updated value in function incrementAttendanceByOne 26
05-22 10:51:44.066 22737-22737/com.aayush.reboot.nssnitdurgapur I/TAG: Value of daysOnWhichPresent in onCreate method 26
05-22 10:51:44.645 22737-22737/com.aayush.reboot.nssnitdurgapur I/TAG: Value of daysOnWhichPresent in onCreate method 25
ReyAnthonyRenacia
  • 17,219
  • 5
  • 37
  • 56
Aayush Raj
  • 13
  • 5

2 Answers2

0

To solve this, you definitely need to use Firebase Transactions.

When working with data that could be corrupted by concurrent modifications, such as incremental counters, you can use a transaction operation.

To achieve this, please use my answer from this post in which I have explained how you can increment a value in Firebase Realtime database and how you can read it back.

You also need to know that you cannot use something now that hasn't been loaded yet. With other words, you cannot simply use the daysPresent object, outside the onDataChange() method because it will always be null due the asynchronous behaviour of this method. This means that by the time you are trying to use that result outside that method, the data hasn't finished loading yet from the database and that's why is not accessible and you are always getting zero.

A quick solve for this problem would be to use daysPresent only inside the onDataChange() method, or if you want to use it outside, I recommend you dive into the asynchronous world and see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • I have used the transaction method as in first link. Though it is reading the correct value but still not able to get it updated in database. – Aayush Raj May 21 '18 at 17:52
  • It should. Are you sure you are using the right reference? – Alex Mamo May 21 '18 at 18:00
  • Yeah, My log shows that the value has been updated successfully, but this is not updating in database. I have used two Logs, one before updating the value and one after, both are providing the result as it should be, but in database it is not reflected. – Aayush Raj May 21 '18 at 18:11
  • If you logs are write then you should see the changes in your database. Are you sure you are looking in the right place? The code above works 100%, I have tested this code myself. – Alex Mamo May 21 '18 at 18:12
  • Yes, I have rechecked by changing the value in database. It is reading the correct value and also logging the correct result. Don't know why it is not writing to the database. – Aayush Raj May 21 '18 at 18:24
  • Please share the changed code by adding it to your question, to see the issue more clearly. – Alex Mamo May 21 '18 at 18:25
  • I understand now what you are talking about. As I see, your code is correct. Your log statements are also working perfectly fine and this is because are placed inside the transaction. If you want to use `daysPresent`, you can use it only inside the transaction where you are using those log statements, you cannot simply declare it as global and use it outside. So with other words, you cannot simply call it from the `onCreate` method, it will always be `null`. That's why I wrote you the second part of my answer. See the post. Does it work when you are using `daysPresent` inside the transaction? – Alex Mamo May 22 '18 at 07:30
  • Yes, It is now working perfectly fine. Earlier, I have some rules in database for preventing duplicate data and I completely forgot about that. Now when those rules have been removed, it is working like charm. Thank You! – Aayush Raj May 22 '18 at 10:00
0

I think you should first get the proper value of node and then update the same after incrementing it by 1.

Please try something like this :

yourChildRef.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {
                        if (dataSnapshot != null) {
                            String toUserCounts = String.valueOf(dataSnapshot.getValue() == null ? "0" : dataSnapshot.getValue());  // Fetch value of node
                            Log.e("day value:" , toUserCounts); // Print value of node

                            HashMap<String, Object> params = new HashMap<>();
                            params.put("daysOnWhichPresent", Integer.parseInt(toUserCounts)+1);    // increment daysOnWhichPresent by 1

                           yourDatabaseChildReference.updateChildren(params);    // Update value on node
                        }
                    }

                    @Override
                    public void onCancelled(DatabaseError databaseError) {

                    }
                });
Deep Patel
  • 2,584
  • 2
  • 15
  • 28