2

I am using Android and I would like to store some data in the Firebase Realtime Database. Strangely, sometimes this works and sometimes it does not. I thought that this might have something to do with my WLAN connection which is not always good, but even if the WLAN connection is good sometimes data can be written into the Firebase Database, and sometimes it just does not work.

Anyway, what I would like to have is a message to the user that the data has not been written into the Firebase Database if this was the case in form of a toast. For this purpose I have the following Java Fragment:

package com.example.td.bapp;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.Bundle;
import android.text.InputType;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.navigation.Navigation;

import com.example.td.bapp.databinding.TestFragmentBinding ;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicBoolean;


    public class TestFragment extends Fragment implements View.OnClickListener {
    
    
    
        @Override
        public void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
        }
    
        @Nullable
    
        private TestFragmentBinding binding;
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    
            binding = TestFragmentBinding.inflate(inflater, container, false);
    
    
            /*
            Set onClickListeners to the buttoms
             */
    
            binding.orderingButton.setOnClickListener(this);
    
            return binding.getRoot();
    
        }
    
        public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
    
    
    
        }
    
        @Override
        public void onClick(View view) {
    
            if(view.getId() == R.id.ordering_button) {
    
    
                /*
                Write data into the Firebase DB
                 */
    
                long currentTimeMillis = System.currentTimeMillis();
                SimpleDateFormat sdf1 = new SimpleDateFormat("dd-MM-yy");
                sdf1.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
                SimpleDateFormat sdf2 = new SimpleDateFormat("HH-mm-ss");
                sdf1.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
    
    
                DatabaseReference rootRef = FirebaseDatabase.getInstance("https://drink-server-db-default-rtdb.europe-west1.firebasedatabase.app").getReference();
                DatabaseReference ordersRef = rootRef.child("orders");
                String id ="table_" + MainActivity.tableNumber + "_order_" + MainActivity.orderNumberOfTheSession + "_date_" + sdf1.format(new Date()) + "_time_" + sdf2.format(new Date());
                String name1 = "Test_1";
                String name2 = "Test_2";
                FirebaseDBItem_Order currentOrder = new FirebaseDBItem_Order(name1, name2);
                Log.e("LogTag", "Before write into DB");
                ordersRef.child(id).setValue(currentOrder).addOnCompleteListener(new OnCompleteListener<Void>() {
                    @Override
                    public void onComplete(@NonNull Task<Void> task) {
                        if (task.isSuccessful()) {
                            Log.e("dbTAG",  "Data successfully written.");
                            int duration = Toast.LENGTH_LONG;
                            Toast toast = Toast.makeText(getContext(), getString(R.string.message_orderSubmittedSuccessfully), duration);
                            toast.setGravity(Gravity.CENTER, 0, 0);
                            toast.show();
                        }
                        else {
                            Log.e("dbTAG", task.getException().getMessage());
                            int duration = Toast.LENGTH_LONG;
                            Toast toast = Toast.makeText(getContext(), getString(R.string.message_orderSubmittedNotSuccessfully), duration);
                            toast.setGravity(Gravity.CENTER, 0, 0);
                            toast.show();
                        }
                    }
                });
    
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.e("LogTag", "After write into DB");
    
    
                Navigation.findNavController(getView()).navigate(TestFragmentDirections.actionFRCocktailToFRMenu());
    
            }
    
    
        }
    
    
    }

If the data is written successfully into the Firebase Database the toast is displayed and I get the correct dbTAG and LogTag messages:

E/LogTag: Before write into DB

E/LogTag: After write into DB

E/dbTAG: Data successfully written.

However, when the data is not successfully written into the Firebase Database, I just don't get any message from the dbTAG of the onComplete method and no new entry can be found in the database. I just get the LogTag messages:

E/LogTag: Before write into DB

E/LogTag: After write into DB

Now actually I have 2 questions:

  1. Why sometimes the data can be written into the Firebase Database and sometimes not and why this seems to happen "randomly"? Can you think of some reasons?
  2. More importantly: How can I get an error message when the data could not be written into the Firebase Database such that I can inform the user? And related to this: Why do I not get a Log.e("dbTAG", task.getException().getMessage()); message when the data was not successfully written into the Firebase Database? So the else branch of the onComplete method is not executed when the data is not successfully stored.

If you need any further information, please let me know.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
VanessaF
  • 515
  • 11
  • 36

1 Answers1

3

Why sometimes the data can be written into the Firebase Database and sometimes not and why this seems to happen "randomly"? Can you think of some reasons?

Most likely this it's because of the lack of internet connection. To solve this, you should enable offline persistence. In Java code, this can be done using the following line:

FirebaseDatabase.getInstance().setPersistenceEnabled(true);

According to the docs:

Firebase applications work even if your app temporarily loses its network connection.

Firebase apps automatically handle temporary network interruptions. Cached data is available while offline and Firebase resends any writes when network connectivity is restored.

And to answer your second question:

More importantly: How can I get an error message when the data could not be written into the Firebase Database such that I can inform the user? And related to this: Why do I not get a Log.e("dbTAG", task.getException().getMessage()); message when the data was not successfully written into the Firebase Database? So the else branch of the onComplete method is not executed when the data is not successfully stored.

You are handling the errors in a correct way using:

Log.e("dbTAG", task.getException().getMessage());

You'll get an error message, when there is a problem with writing the data, meaning the write operation is rejected by the Firebase severs, for example, when you have improper security rules.

Please also note, that Firebase Realtime Database SDK doesn't throw an Exception when there is no internet connection, and it makes sense since Firebase Realtime Database is designed to work offline. Behind the scenes, Firebase Realtime Database SDK tries to reconnect until the devices regain connectivity. However, it will indeed throw an Exception, when Firebase servers reject the request due to a security rule issue.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • Thanks Alex for your answer. I have several remarks on that: 1) Sometime the data is not written into the Firebase Database even if I have a good Internet connection. Can you think of any other reason why this is happening? 2) Even without inserting your suggested code `FirebaseDatabase.getInstance().setPersistenceEnabled(true);` sometimes when writting the orders could not be done, after some time many orders where written into the database (so they were somehow in a cache). So is this the standard behaviour (even without the code) and why did this only sometimes happen and sometimes not? – VanessaF Nov 07 '21 at 09:25
  • 1. Maybe the operation doesn't complete, as it takes too much time for that. Try to [constantly check for internet connectivity](https://stackoverflow.com/questions/52279144/how-to-verify-if-user-has-network-access-and-show-a-pop-up-alert-when-there-isn). 2. If those writes remain in the cache when the device regains connectivity, all the writers will be sent to Firebase servers. – Alex Mamo Nov 07 '21 at 09:29
  • Besides that, don't put the main thread to sleep. That's also a cause that can provide that behavior. – Alex Mamo Nov 07 '21 at 09:30
  • Thanks Alex for your answer. So is this persistant behaviour `FirebaseDatabase.getInstance().setPersistenceEnabled(true);` enables by default? If not, where exactly do I have to insert it? In the main activity or in the fragment (I got some error messages when inserting it into the Fragment). Any where and why would you advice me to constantly check for internet connections? Before writing into the Firebase Database? – VanessaF Nov 07 '21 at 09:36
  • Also when putting your suggested statement in the main activity I sometimes get the error " Caused by: com.google.firebase.database.DatabaseException: Calls to setPersistenceEnabled() must be made before any other usage of FirebaseDatabase instance." You wrote (regarding my 2nd question ): "If those writes remain in the cache when the device regains connectivity, all the writers will be sent to Firebase servers." --> How can I make sure that they remain in the cache, because in previous runs sometimes this happened and sometimes it did not happen. – VanessaF Nov 07 '21 at 09:44
  • You have to add that line of code in the activity that starts first. You have to check for internet connectivity to be sure you can write to Firebase. Assuming only that you have a healthy connection without actually testing it is not enough. Yes, before writing, so you can be sure the internet is ok, before actually performing the write operation. – Alex Mamo Nov 07 '21 at 09:51
  • Then you should do what the error message says, *" Caused by: com.google.firebase.database.DatabaseException: Calls to setPersistenceEnabled() must be made before any other usage of FirebaseDatabase instance.*. – Alex Mamo Nov 07 '21 at 09:52
  • If you are using that line of code, while offline, you are always writing to the cache. If you consider at some point in time to try using [Cloud Firestore](https://firebase.google.com/docs/firestore/), then you can specify also the [source](https://firebase.google.com/docs/firestore/query-data/get-data#source_options). – Alex Mamo Nov 07 '21 at 09:54
  • Thanks Alex for your great help. I really appreciate it. I upvoted and accepted your answer. – VanessaF Nov 07 '21 at 10:14