FirebaseDatabase.getInstance().setPersistenceEnabled(true) 究竟是做什么的?

3

我好像不太理解setPersistenceEnabled

我原本以为通过调用

FirebaseDatabase.getInstance().setPersistenceEnabled(true);

作为我 Android 应用程序对象中的第一件事情,我希望能够在离线模式下读写 Firebase 数据,并且我的数据将被本地存储,因此即使在离线模式下,下次应用程序启动时也可以使用。
我认为,在离线模式下进行的写入操作会在网络再次可用时与 FB 数据库同步。
但实际上并非如此。
在飞行模式下完成写入操作,但似乎无法读取该数据。
addListenerForSingleValueEvent 和 ValueEventListener 不能检测到没有网络的数据库写入。
请有人帮助我理解 setPersistenceEnabled(true) 的作用以及它的典型用例是什么?
补充说明:
我编写了我能想到的最小可能的 Android Firebase 数据库应用程序。
以下是应用程序类:
 import android.app.Application;
import android.util.Log;

import com.google.firebase.database.FirebaseDatabase;

public class TheApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        // See https://firebase.google.com/docs/reference/android/com/google/firebase/database/FirebaseDatabase.html#public-methods
        FirebaseDatabase.getInstance().setPersistenceEnabled(true);
        Log.d("TheApp", "application created");
    }
}

主活动所做的就是在EditText旁边显示一个“保存”按钮。 在onCreate中,MainActivity登录一个已知用户。当用户点击保存按钮时,EditText中的字符串将被写入Firebase。
package com.grayraven.simpledb;

import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

public class MainActivity extends AppCompatActivity {
    FirebaseAuth mAuth;
    FirebaseUser mUser;
    ValueEventListener mListener;
    private FirebaseAuth.AuthStateListener mAuthListener;
    private static final String TAG = "Main";
    FirebaseDatabase db = FirebaseDatabase.getInstance();
    DatabaseReference dbRef;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        dbRef = db.getReference("/data/strings/");

        Button btnSave = (Button)findViewById(R.id.btn_save);
        btnSave.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EditText editText = (EditText)findViewById(R.id.editText);
                String text = String.valueOf(editText.getText());
                dbRef.setValue(text);
            }
        });

        signIn();

    }

    private void signIn() {
        mAuth= FirebaseAuth.getInstance();
        String email = "jim@example.com";  //valid credentials
        String password = "12345678";
        mAuth.signInWithEmailAndPassword(email, password)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        Log.d(TAG, "signInWithEmail:onComplete:" + task.isSuccessful());

                        // If sign in fails, display a message to the user. If sign in succeeds
                        // the auth state listener will be notified and logic to handle the
                        // signed in user can be handled in the listener.
                        if (!task.isSuccessful()) {
                       //always fails when offline
                            Log.w(TAG, "signInWithEmail failed", task.getException());  
                        }
                    }
                });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

只要设备连接到网络,所有这些都可以正常工作。
如果应用程序在没有网络连接的情况下启动,则登录失败,因此用户无法执行任何操作。
如果应用程序在运行时网络连接中断,则不会保留任何保存的字符串,并在网络恢复时上传到 Firebase。
这是预期的行为吗?这种行为似乎与以下引用的文档不一致:
Firebase 数据库客户端将缓存同步数据并跟踪您在应用程序运行时发起的所有写入操作。它可以无缝处理间歇性的网络连接,并在网络连接恢复时重新发送写入操作。但是,默认情况下,您的写入操作和缓存数据仅存储在内存中,并且在应用程序重新启动时将丢失。通过将此值设置为 true,数据将持久保存到设备(磁盘)存储器中,并在应用程序重新启动时再次可用(即使此时没有网络连接)。请注意,必须在创建第一个数据库引用之前调用此方法,并且每个应用程序只需要调用一次。

嘿,吉姆。你能否提供一个最小化且完整的代码片段来重现问题?这里有很多细节,我们中的大多数人更善于阅读代码而不是文字。 - Frank van Puffelen
2个回答

3

我的问题其实很简单。虽然我有我的TheApp类,但我忘记更新清单文件,使得TheApp被首先调用。你可以使用“name”应用程序标签来实现:

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        android:name="com.grayraven.simpledb.TheApp"> <!-- I forgot this! -->

现在应用程序似乎在线或离线都可以正常工作,因为实际上已调用setPersistenceEnabled(true)。唯一的限制是,如果您注销然后失去了网络连接,则无法进行身份验证。但是,如果您已经过身份验证,似乎您的应用程序将可以离线工作,即使重新启动它也是如此。至少看起来是这样。


2
将近三年过去了,我仍然面临着同样的问题,却没有任何线索。 :-( - Guanaco Devs

0

我遇到了同样的问题。解决方法是在监听器完成时使用 keepSynced(true) 方法,然后改为 false。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接