Home > Mobile >  How to get feedback from Firebase Realtime Database about writing operation in Android
How to get feedback from Firebase Realtime Database about writing operation in Android

Time:10-23

I have an app with about 20 Fragments in Android and each of them can write Data into the Firebase Realtime Database. For not implementing the write operation in each of those 20 Fragments, I have created a HelpFunctionsclass that has the method writeDataIntoFirebaseDB

public class HelpFunctions {    
    static boolean dataCouldBeWrittenIntoFirebaseDB;

    public static boolean writeDataIntoFirebaseDB (Object dataBaseEntry, String dbTable, String entryIdPrefix) {

        dataCouldBeWrittenIntoFirebaseDB = false;
        long currentTimeMillis = System.currentTimeMillis();
        SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yy");
        sdf1.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
        SimpleDateFormat sdf2 = new SimpleDateFormat("HH-mm-ss");
        sdf2.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));

        DatabaseReference rootRef = FirebaseDatabase.getInstance("https://...").getReference();
        DatabaseReference ordersRef = rootRef.child(dbTable);

        String entryIdComplete =entryIdPrefix   "_date_"   sdf1.format(new Date())   "_time_"   sdf2.format(new Date());
        ordersRef.child(entryIdComplete).setValue(dataBaseEntry).addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                if (task.isSuccessful()) {
                    Log.e("HelpFuncTag",  "Data successfully written.");

                    dataCouldBeWrittenIntoFirebaseDB = true;
                }
                else {
                    Log.e("HelpFuncTag", task.getException().getMessage());
                    dataCouldBeWrittenIntoFirebaseDB  = false;
                }
            }
        });

        return dataCouldBeWrittenIntoFirebaseDB ;
    }
}

The method is called from the 20 Fragments and it should return true or false to the fragments if the writing operation was successfull to Firebase, such that the Fragments can show a message (Snackbar) to the user. When using this approach, the method always returns false, even though the data could be written into Firebase because of the asynchronous operation of Firebase.

This is why I tried to implement a callback similarly as explained in this video: https://www.youtube.com/watch?v=OvDZVV5CbQg&ab_channel=AlexMamo

So I have the following code:

public class HelpFunctions {
public static boolean writeDataIntoFirebaseDB (Object dataBaseEntry, String dbTable, String entryIdPrefix) {

        final boolean[] dataCouldBeWrittenIntoFirebaseDB = {false};
        long currentTimeMillis = System.currentTimeMillis();
        SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yy");
        sdf1.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
        SimpleDateFormat sdf2 = new SimpleDateFormat("HH-mm-ss");
        sdf2.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));

        DatabaseReference rootRef = FirebaseDatabase.getInstance("https://...").getReference();
        DatabaseReference ordersRef = rootRef.child(dbTable);

        String entryIdComplete =entryIdPrefix   "_date_"   sdf1.format(new Date())   "_time_"   sdf2.format(new Date());

        HelpFunctions.writeIntoFirebase(new HelpFunctions.FirebaseCallback() {
            @Override
            public void onCallBack(boolean writingSuccesfull) {
                if (writingSuccesfull==true) {
                    dataCouldBeWrittenIntoFirebaseDB[0] =  true;
                }
                if (writingSuccesfull==false) {
                    dataCouldBeWrittenIntoFirebaseDB[0] =  false;
                }
            }
        }, entryIdComplete, dataBaseEntry, ordersRef);

        return dataCouldBeWrittenIntoFirebaseDB[0];
    }

    private static void writeIntoFirebase (HelpFunctions.FirebaseCallback firebaseCallback, String entryIdComplete, Object dataBaseEntry, DatabaseReference ordersRef ) {
        boolean dataCouldBeWrittenIntoFirebaseDB = false;
        ordersRef.child(entryIdComplete).setValue(dataBaseEntry).addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                if (task.isSuccessful()) {
                    Log.e("HelpFuncTag",  "Data successfully written.");
                    firebaseCallback.onCallBack (true);
                }
                else {
                    Log.e("HelpFuncTag", task.getException().getMessage());
                    firebaseCallback.onCallBack (false);    
                }
            }
        });
    }//End write data method

    private interface FirebaseCallback  {
        void onCallBack(boolean dataCouldBeWrittenIntoFirebase);
    }
}

Unfortunately, also the Callback solution always returns false to the Fragments altough the data could be written into the Firebase database. Do you know another approach how to get feedback from Firebase Realtime Database about writing operation in Android?

CodePudding user response:

Data is written to Firebase (and most modern cloud APIs) asynchronously. It's easiest to see what this means by adding some logging to your code:

Log.i("HelpFuncTag", "Before starting to write");
ordersRef.child(entryIdComplete).setValue(dataBaseEntry).addOnCompleteListener(new OnCompleteListener<Void>() {
    @Override
    public void onComplete(@NonNull Task<Void> task) {
        Log.i("HelpFuncTag", "Done writing");    
    }
});
Log.i("HelpFuncTag", "After starting to write");    

The output of this is:

Before starting to write

After starting to write

Done writing

This is probably not the order your expected the output in, but it is working as designed - and it completely explains why the code always returns true. The dataCouldBeWrittenIntoFirebaseDB[0] = false hasn't run yet by the time your function returns a value.


The solution for this problem is always the same: any code that needs to run after the data has been written, has to be inside the onComplete function, be called from there, or be otherwise synchronized.

In fact, that's precisely why you have to call addOnCompleteListener to begin with. If anything else was possible, setValue would have simply returned the result instead of requiring an OnCompleteListener.

The common solution would thus be to pass a (custom) callback to your writeDataIntoFirebaseDB that takes a boolean, and then calling that from OnComplete. For an example of this and more information, see: getContactsFromFirebase() method return an empty list

CodePudding user response:

refactor your HelpFunctions class as below. Remove static keyword from the function, make the interface public and create a property for that interface.

public class HelpFunctions {

    FirebaseCallback firebaseCallback;

    public HelpFunctions(FirebaseCallback callback) {
        this.firebaseCallback = callback;
    }

    public void writeDataIntoFirebaseDB (Object dataBaseEntry, String dbTable, String entryIdPrefix) {

        final boolean[] dataCouldBeWrittenIntoFirebaseDB = {false};
        long currentTimeMillis = System.currentTimeMillis();
        SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yy");
        sdf1.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
        SimpleDateFormat sdf2 = new SimpleDateFormat("HH-mm-ss");
        sdf2.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));

        DatabaseReference rootRef = FirebaseDatabase.getInstance("https://...").getReference();
        DatabaseReference ordersRef = rootRef.child(dbTable);

        String entryIdComplete =entryIdPrefix   "_date_"   sdf1.format(new Date())   "_time_"   sdf2.format(new Date());

        ordersRef.child(entryIdComplete).setValue(dataBaseEntry).addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                if (task.isSuccessful()) {
                    Log.e("HelpFuncTag",  "Data successfully written.");
                    firebaseCallback.onCallBack(true);
                }
                else {
                    Log.e("HelpFuncTag", task.getException().getMessage());
                    firebaseCallback.onCallBack(false);
                }
            }
        });
    }


    public interface FirebaseCallback  {
        void onCallBack(boolean dataCouldBeWrittenIntoFirebase);
    }
}

And in your fragments implements FirebaseCallback and create a HelpFunctions objects like below.

public class SampleFragment extends Fragment implements HelpFunctions.FirebaseCallback {

    HelpFunctions helpFunctions;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        helpFunctions = new  HelpFunctions(this);
    }

    @Override
    public void onCallBack(boolean dataCouldBeWrittenIntoFirebase) {
        // Handle your fragment logic here
    }
}
  • Related