无法使用 Android 将数据写入 Firebase 数据库时如何获取错误消息

Posted

技术标签:

【中文标题】无法使用 Android 将数据写入 Firebase 数据库时如何获取错误消息【英文标题】:How to get an error message when data could not be written into the Firebase Database using Android 【发布时间】:2021-12-19 17:42:36 【问题描述】:

我使用的是 android,我想在 Firebase 实时数据库中存储一些数据。奇怪的是,有时这有效,有时却无效。我认为这可能与我的 WLAN 连接并不总是很好有关,但即使 WLAN 连接良好,有时数据也可以写入 Firebase 数据库,但有时它就无法正常工作。

无论如何,我希望以敬酒的形式向用户发送一条消息,说明数据尚未写入 Firebase 数据库。为此,我有以下 Java 片段:

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());
    
            
    
    
        
    
    
    

如果数据成功写入 Firebase 数据库,则会显示 toast,并且我会收到正确的 dbTAG 和 LogTag 消息:

E/LogTag:写入数据库之前

E/LogTag:写入数据库后

E/dbTAG:数据写入成功。

但是,当数据没有成功写入 Firebase 数据库时,我只是没有从 onComplete 方法的 dbTAG 收到任何消息,并且在数据库中找不到新条目。我刚刚收到 LogTag 消息:

E/LogTag:写入数据库之前

E/LogTag:写入数据库后

现在实际上我有两个问题:

    为什么有时数据可以写入 Firebase 数据库,有时却不能,为什么这似乎是“随机”发生的?你能想出一些原因吗? 更重要的是:当数据无法写入 Firebase 数据库时,如何获得错误消息以便通知用户?与此相关:当数据未成功写入 Firebase 数据库时,为什么我没有收到 Log.e("dbTAG", task.getException().getMessage()); 消息?所以onComplete方法的else分支在数据没有成功存储的时候不会执行。

如果您需要任何进一步的信息,请告诉我。

【问题讨论】:

【参考方案1】:

为什么有时数据可以写入 Firebase 数据库,而有时却不能,为什么这似乎是“随机”发生的?你能想到一些原因吗?

这很可能是因为缺乏互联网连接。要解决这个问题,您应该启用离线持久性。在 Java 代码中,这可以使用以下行来完成:

FirebaseDatabase.getInstance().setPersistenceEnabled(true);

根据文档:

即使您的应用暂时失去网络连接,Firebase 应用也能正常工作。

Firebase 应用会自动处理临时网络中断。缓存数据在离线时可用,并且 Firebase 会在网络连接恢复时重新发送任何写入。

回答你的第二个问题:

更重要的是:当数据无法写入 Firebase 数据库时,如何获得错误消息以便通知用户?与此相关:为什么我没有得到 Log.e("dbTAG", task.getException().getMessage());数据未成功写入 Firebase 数据库时的消息?所以onComplete方法的else分支在数据存储不成功时不会执行。

您正在以正确的方式处理错误:

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

当写入数据出现问题时,您会收到一条错误消息,这意味着写入操作被 Firebase 服务器拒绝,例如,当您的安全规则不正确时。

还请注意,Firebase 实时数据库 SDK不会在没有互联网连接时抛出异常,这是有道理的,因为 Firebase 实时数据库旨在离线工作。在幕后,Firebase 实时数据库 SDK 会尝试重新连接,直到设备重新连接。但是,当 Firebase 服务器由于安全规则问题而拒绝请求时,它确实会抛出异常。

【讨论】:

感谢亚历克斯的回答。我对此有几点意见:1)有时即使我有良好的互联网连接,数据也不会写入 Firebase 数据库。你能想到发生这种情况的任何其他原因吗? 2)即使没有插入您建议的代码FirebaseDatabase.getInstance().setPersistenceEnabled(true);,有时在编写订单时也无法完成,一段时间后,许多订单被写入数据库(因此它们以某种方式在缓存中)。那么这是标准行为(即使没有代码),为什么有时会发生,有时不会? 1.也许操作没有完成,因为它需要太多时间。尝试constantly check for internet connectivity。 2. 如果设备恢复连接时这些写入仍保留在缓存中,则所有写入器都将发送到 Firebase 服务器。 除此之外,不要让主线程进入睡眠状态。这也是可以提供这种行为的原因。 感谢亚历克斯的回答。那么这种持久行为FirebaseDatabase.getInstance().setPersistenceEnabled(true); 默认启用吗?如果没有,我到底应该在哪里插入它?在主要活动或片段中(将其插入片段时收到一些错误消息)。你会在哪里以及为什么建议我经常检查互联网连接?在写入 Firebase 数据库之前? 此外,当您将建议的语句放在主要活动中时,我有时会收到错误“原因:com.google.firebase.database.DatabaseException:必须在任何其他使用之前调用 setPersistenceEnabled() FirebaseDatabase 实例。”您写道(关于我的第二个问题):“如果设备重新连接时这些写入仍保留在缓存中,则所有写入器都将被发送到 Firebase 服务器。” --> 我怎样才能确保它们保留在缓存中,因为在以前的运行中有时会发生这种情况,有时不会发生。

以上是关于无法使用 Android 将数据写入 Firebase 数据库时如何获取错误消息的主要内容,如果未能解决你的问题,请参考以下文章

Android *** 服务 Bytebuffer 无法写入

无法写入导入的 SQLite 数据库 Android

Android Studio:无法写入外部数据库

(Android和Firebase)无法解析符号“dataSnapshot”[关闭]

无法从 Android 中 FirebaseMessagingService 的 onMessageReceived 生成通知

将 PCM 录制的数据写入 .wav 文件(java android)