When working with Firebase, you need to remember that your code is asynchronous.
Integer tot = 0;
Integer rem = 0;
Integer avl = 0;
DatabaseReference reference;
reference = FirebaseDatabase.getInstance().getReference("Received").child("Gokul");
reference.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) { }
@Override
public void onCancelled(@NonNull DatabaseError error) { }
});
reference = FirebaseDatabase.getInstance().getReference("Remaining").child("Gokul");
reference.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) { }
@Override
public void onCancelled(@NonNull DatabaseError error) { }
});
avl = tot - rem; // this line runs before any of onDataChange/onCancelled handlers above
amount3.setText(String.valueOf(avl));
Next, don't reuse reference
like you have. Store them as separate variables so you don't confuse yourself or anyone else who works with that code.
DatabaseReference receivedGokulRef = FirebaseDatabase.getInstance().getReference("Received").child("Gokul");
DatabaseReference remainingGokulRef = FirebaseDatabase.getInstance().getReference("Remaining").child("Gokul");
Next, you need to make sure that both receivedGokulRef
and remainingGokulRef
get their data first before calculating the balance.
If you are expecting this avl
value to get realtime updates, inside of the listeners you add using the Query#addValueEventListener()
method, update their respective amount1
and amount2
values and then call a refresh/recalculate method for amount3
.
amount3.setText("Loading...");
DatabaseReference receivedGokulRef = FirebaseDatabase.getInstance().getReference("Received").child("Gokul");
receivedGokulRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
Integer sum = 0;
for (DataSnapshot snap : snapshot.getChildren()) {
Integer value = snap.child("amount").getValue(Integer.class);
sum += value;
}
amount1.setText(String.valueOf(sum));
refreshAvaliableBalance(); // <-- trigger calculation
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
Toast.makeText(DetailsActivity.this, error.getMessage(), Toast.LENGTH_SHORT).show();
}
});
DatabaseReference remainingGokulRef = FirebaseDatabase.getInstance().getReference("Remaining").child("Gokul");
remainingGokulRef.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot snapshot) {
Integer sum = 0;
for (DataSnapshot snap : snapshot.getChildren()) {
Integer value = snap.child("amount").getValue(Integer.class);
sum += value;
}
amount2.setText(String.valueOf(sum));
refreshAvaliableBalance(); // <-- trigger calculation
}
@Override
public void onCancelled(@NonNull DatabaseError error) {
Toast.makeText(DetailsActivity.this, error.getMessage(), Toast.LENGTH_SHORT).show();
}
});
/* as a method of DetailsActivity */
private void refreshAvaliableBalance() {
String amount1Text = amount1.getText();
String amount2Text = amount2.getText();
if (amount1Text == "" || amount2Text == "") {
// still loading
amount3.setText("Loading...");
return;
}
Integer receivedSum = Integer.parseInt(amount1Text);
Integer remainingSum = Integer.parseInt(amount2Text);
Integer available = receivedSum - remainingSum;
amount3.setText(String.valueOf(available));
}
If you are only retrieving the balance once you could use Query#get()
which returns a Task
containing your data. You can then combine this with the Tasks
utility methods, you can chain actions together.
For receivedGokulRef
, get its value and sum it together. For remainingGokulRef
, get its value and sum it together. Once we have both sums, calculate the difference.
Task<Integer> receivedGokulSumTask = receivedGokulRef.get()
.onSuccessTask(new SuccessContinuation<DataSnapshot, Integer>() {
@NonNull
@Override
public Task<Integer> then(DataSnapshot querySnapshot) {
Integer sum = 0;
for (DataSnapshot childSnap : querySnapshot.getChildren()) {
Integer value = snap.child("amount").getValue(Integer.class);
sum += value;
}
return sum;
}
});
Task<Integer> remainingGokulSumTask = remainingGokulRef.get()
.onSuccessTask(new SuccessContinuation<DataSnapshot, Integer>() {
@NonNull
@Override
public Task<Integer> then(DataSnapshot querySnapshot) {
Integer sum = 0;
for (DataSnapshot childSnap : querySnapshot.getChildren()) {
Integer value = snap.child("amount").getValue(Integer.class);
sum += value;
}
return sum;
}
});
Tasks.whenAllComplete([receivedGokulSumTask, remainingGokulSumTask])
.addOnSuccessListener(new OnSuccessListener<List<Task<Integer>>>() {
// Tasks.whenAllComplete never fails, so we can use addOnSuccessListener for simplicity
@NonNull
@Override
public void onSuccess(List<Task<Integer>> sumTasks) {
Task<Integer> receivedSumTask = sumTasks.get(0);
Task<Integer> remainingSumTask = sumTasks.get(1);
if (receivedSumTask.isSuccessful() && remainingSumTask.isSuccessful()) {
// got data properly
Integer receivedSum = receivedSumTask.getResult();
Integer remainingSum = remainingSumTask.getResult();
Integer available = receivedSum - remainingSum; // throw error when negative?
amount1.setText(String.valueOf(receivedSum)); // TODO: rename amount1,amount2,amount3
amount2.setText(String.valueOf(remainingSum));
amount3.setText(String.valueOf(available));
return; // done
}
// if here, either or both tasks failed.
Exception receivedSumException = receivedSumTask.getException();
Exception remainingSumException = remainingSumTask.getException();
StringBuilder messageBuilder = new StringBuilder("Error(s) fetching data");
if (receivedSumException !== null) {
messageBuilder.append("; Error while getting received total sum: " + receivedSumException.getMessage());
}
if (remainingSumException !== null) {
messageBuilder.append("; Error while getting remaining total sum: " + receivedSumException.getMessage());
}
Toast.makeText(DetailsActivity.this, messageBuilder.toString(), Toast.LENGTH_SHORT).show();
}
})