I have an Android Fragment class that has a method to write something on an external firebase database after a button is clicked.
public class FR_Fragment extends Fragment implements View.OnClickListener {
public void onClick(View view) {
boolean writingSuccessfull = false;
boolean writingNotSuccessfull = false;
firebase_DB.child(id).setValue(currentOrder).addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
writingSuccessfull = true;
writingNotSuccessfull = false;
Log.e("dbTAG", "Data successfully written.");
}
else {
writingSuccessfull = false;
writingNotSuccessfull = true;
Log.e("dbTAG", task.getException().getMessage());
}
}
});
}
}
However, I somehow can't access the boolean variables writingSuccessfull
and writingNotSuccessfull
and that I define in the method onClick in the OnCompleteListener. With the shown code I get an error "Variable 'writingSuccessfull' is accessed from within inner class, needs to be final or effectively final"
When I let Android Studio fix this problem the code looks like this:
public class FR_Fragment extends Fragment implements View.OnClickListener {
public void onClick(View view) {
final boolean[] writingSuccessfull = { false };
final boolean[] writingNotSuccessfull = { false };
firebase_DB.child(id).setValue(currentOrder).addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
writingSuccessfull[0] = true;
writingNotSuccessfull[0] = false
Log.e("dbTAG", "Data successfully written.");
}
else {
writingSuccessfull[0] = false;
writingNotSuccessfull[0] = true
Log.e("dbTAG", task.getException().getMessage());
}
}
});
}
}
Now I can execute the code, but the values of the booleans do not change, even if the code inside the listener is executed. I also tried it without the "final" before the boolean variables. But this did not change anything. I can't modify the boolean variables inside this listener. But this is something that I need to do because I would like to know whether the data was written successfully into the database.
Any idea why I get this problem and how to solve it? I'll appreciate every comment.
Update: Here is the updated code with volatile and AtomicBooleas:
public class FR_Fragment extends Fragment implements View.OnClickListener {
private volatile AtomicBoolean writingOrderToTheDatabaseWasSuccessful;
private volatile AtomicBoolean writingOrderToTheDatabaseWasNotSuccessful;
...
public void onClick(View view) {
int internalCounterAttempsWriteDataInTheFBDatabase =0;
writingOrderToTheDatabaseWasSuccessful = new AtomicBoolean (false);
writingOrderToTheDatabaseWasNotSuccessful = new AtomicBoolean (false);
while(writingOrderToTheDatabaseWasSuccessful.get() == false && writingOrderToTheDatabaseWasNotSuccessful.get()==false) {
internalCounterAttempsWriteDataInTheFBDatabase++;
Log.e("LogTag", "internalCounterAttempsWriteDataInTheFBDatabase: " +internalCounterAttempsWriteDataInTheFBDatabase );
Log.e("LogTag", "writingOrderToTheDatabaseWasSuccessful: " + writingOrderToTheDatabaseWasSuccessful.get());
Log.e("LogTag", "writingOrderToTheDatabaseWasNotSuccessful: " + writingOrderToTheDatabaseWasNotSuccessful.get());
DatabaseReference rootRef = FirebaseDatabase.getInstance("https://drink-server-db-default-rtdb.europe-west1.firebasedatabase.app").getReference();
DatabaseReference ordersRef = rootRef.child("orders");
String id =...;
FirebaseDBItem_Order currentOrder = ...;
ordersRef.child(id).setValue(currentOrder).addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(@NonNull Task<Void> task) {
if (task.isSuccessful()) {
writingOrderToTheDatabaseWasSuccessful.set(true);
writingOrderToTheDatabaseWasNotSuccessful.set(false);
Log.e("dbTAG", "Data successfully written.");
Log.e("LogTag", "Listener: writingOrderToTheDatabaseWasSuccessful: " + writingOrderToTheDatabaseWasSuccessful.get());
Log.e("LogTag", "Listener: writingOrderToTheDatabaseWasNotSuccessful: " + writingOrderToTheDatabaseWasNotSuccessful.get());
} else {
Log.e("dbTAG", task.getException().getMessage());
}
}//end method onComplete
});//end addOnCompleteListener
if (internalCounterAttempsWriteDataInTheFBDatabase >=10) {
writingOrderToTheDatabaseWasNotSuccessful.set(true);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}//end while loop
Log.e("LogTag", "EndWhile: writingOrderToTheDatabaseWasSuccessful: " + writingOrderToTheDatabaseWasSuccessful.get());
Log.e("LogTag", "EndWhile: writingOrderToTheDatabaseWasNotSuccessful: " + writingOrderToTheDatabaseWasNotSuccessful.get());
//Show a toast about the order
if (writingOrderToTheDatabaseWasSuccessful.get() ==true) {
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(getContext(), getString(R.string.message_orderSubmittedSuccessfully), duration);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
if (writingOrderToTheDatabaseWasNotSuccessful.get() ==true) {
int duration = Toast.LENGTH_LONG;
Toast toast = Toast.makeText(getContext(), getString(R.string.message_orderSubmittedNotSuccessfully), duration);
toast.setGravity(Gravity.CENTER, 0, 0);
toast.show();
}
Navigation.findNavController(...);
}//end method onClick
}//end class
The problem is the asnychronous call of the onComplete method. The code to change the booleans is not called immediately as it is in the onComplete method that is called asynchronously. This means in my example that the code in the onComplete method is just called after the 10 while-loop iterations and not during the loop as intended. The biggest question now is how to call any code in the onComplete method during the while loop.
Update: Here is the (relevant) output from logcat when running the code:
E/LogTag: internalCounterAttempsWriteDataInTheFBDatabase: 1
writingOrderToTheDatabaseWasSuccessful: false
writingOrderToTheDatabaseWasNotSuccessful: false
E/LogTag: internalCounterAttempsWriteDataInTheFBDatabase: 2
E/LogTag: writingOrderToTheDatabaseWasSuccessful: false
writingOrderToTheDatabaseWasNotSuccessful: false
E/LogTag: internalCounterAttempsWriteDataInTheFBDatabase: 3
writingOrderToTheDatabaseWasSuccessful: false
writingOrderToTheDatabaseWasNotSuccessful: false
E/LogTag: internalCounterAttempsWriteDataInTheFBDatabase: 4
E/LogTag: writingOrderToTheDatabaseWasSuccessful: false
writingOrderToTheDatabaseWasNotSuccessful: false
E/LogTag: internalCounterAttempsWriteDataInTheFBDatabase: 5
E/LogTag: writingOrderToTheDatabaseWasSuccessful: false
writingOrderToTheDatabaseWasNotSuccessful: false
E/LogTag: internalCounterAttempsWriteDataInTheFBDatabase: 6
writingOrderToTheDatabaseWasSuccessful: false
writingOrderToTheDatabaseWasNotSuccessful: false
E/LogTag: internalCounterAttempsWriteDataInTheFBDatabase: 7
E/LogTag: writingOrderToTheDatabaseWasSuccessful: false
writingOrderToTheDatabaseWasNotSuccessful: false
E/LogTag: internalCounterAttempsWriteDataInTheFBDatabase: 8
writingOrderToTheDatabaseWasSuccessful: false
E/LogTag: writingOrderToTheDatabaseWasNotSuccessful: false
E/LogTag: internalCounterAttempsWriteDataInTheFBDatabase: 9
writingOrderToTheDatabaseWasSuccessful: false
writingOrderToTheDatabaseWasNotSuccessful: false
E/LogTag: internalCounterAttempsWriteDataInTheFBDatabase: 10
E/LogTag: writingOrderToTheDatabaseWasSuccessful: false
E/LogTag: writingOrderToTheDatabaseWasNotSuccessful: false
E/LogTag: EndWhile: writingOrderToTheDatabaseWasSuccessful: false
EndWhile: writingOrderToTheDatabaseWasNotSuccessful: true
E/dbTAG: Data successfully written.
E/LogTag: Listener: writingOrderToTheDatabaseWasSuccessful: true
Listener: writingOrderToTheDatabaseWasNotSuccessful: false
E/dbTAG: Data successfully written.
E/LogTag: Listener: writingOrderToTheDatabaseWasSuccessful: true
Listener: writingOrderToTheDatabaseWasNotSuccessful: false
E/dbTAG: Data successfully written.
E/LogTag: Listener: writingOrderToTheDatabaseWasSuccessful: true
Listener: writingOrderToTheDatabaseWasNotSuccessful: false
E/dbTAG: Data successfully written.
E/LogTag: Listener: writingOrderToTheDatabaseWasSuccessful: true
E/LogTag: Listener: writingOrderToTheDatabaseWasNotSuccessful: false
E/dbTAG: Data successfully written.
E/LogTag: Listener: writingOrderToTheDatabaseWasSuccessful: true
Listener: writingOrderToTheDatabaseWasNotSuccessful: false
E/dbTAG: Data successfully written.
E/LogTag: Listener: writingOrderToTheDatabaseWasSuccessful: true
Listener: writingOrderToTheDatabaseWasNotSuccessful: false
E/dbTAG: Data successfully written.
E/LogTag: Listener: writingOrderToTheDatabaseWasSuccessful: true
Listener: writingOrderToTheDatabaseWasNotSuccessful: false
E/dbTAG: Data successfully written.
E/LogTag: Listener: writingOrderToTheDatabaseWasSuccessful: true
E/LogTag: Listener: writingOrderToTheDatabaseWasNotSuccessful: false
E/dbTAG: Data successfully written.
E/LogTag: Listener: writingOrderToTheDatabaseWasSuccessful: true
E/LogTag: Listener: writingOrderToTheDatabaseWasNotSuccessful: false
E/dbTAG: Data successfully written.
E/LogTag: Listener: writingOrderToTheDatabaseWasSuccessful: true
Listener: writingOrderToTheDatabaseWasNotSuccessful: false
The result is that the item is saved 10 times in the firebase database. What I want is to try to store the item once in the firebase db. If this is successfull, the loop should stop and there should be a toast telling that it was successfull. If it was not successfull the loop should try it 10 times (with a delay of 1 second). If even after 10 attemps the item could not be stored in the firebase database, the loop should be stopped and there should be a toast message telling that the item could not be stored.