Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does FirebaseDatabase.getInstance().setPersistenceEnabled(true) really do?

I don't seem to grok setPersistenceEnabled.

I had thought that by calling

FirebaseDatabase.getInstance().setPersistenceEnabled(true);

as the first thing in my Android app object that I could read and write Firebase data locally even in airplane mode, and that my data would be stored locally and would therefore be available the next time the app starts, even in airplane mode.

I thought writes made while the app was offline would synch with the FB database when the network became available again.

That doesn't seem the case at all.

[edited] Writes in airplane mode complete, but that data doesn't seem to available to read.

addListenerForSingleValueEvent and ValueEventListener do not detect database writes with no network.

Can someone please help me understand what setPersistenceEnabled(true) does, and what typical use cases for for it might be?

Edited to add:

I wrote the smallest possible Android Firebase database app I could think of.

Here's the app class:

 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");
    }
}

All the main activity does is display a 'save' button next to an EditText. In onCreate MainActivity logs in a know user. When the user then clicks the save button the string in the EditText is written to 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 = "[email protected]";  //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);
    }
}

This all works as long as the device is connected to the net.

If the app starts with no network connect the login fails, so the user can do nothing.

If the network connection drops while the app is running no saved strings are preserved and uploaded to Firebase when the network comes back.

Is this expected behavior? This behavior doesn't seem consistent with the documentation quoted below:

public synchronized void setPersistenceEnabled (boolean isEnabled)

The Firebase Database client will cache synchronized data and keep track of all writes you've initiated while your application is running. It seamlessly handles intermittent network connections and re-sends write operations when the network connection is restored. However by default your write operations and cached data are only stored in-memory and will be lost when your app restarts. By setting this value to true, the data will be persisted to on-device (disk) storage and will thus be available again when the app is restarted (even when there is no network connectivity at that time). Note that this method must be called before creating your first Database reference and only needs to be called once per application.

like image 904
Jim In Texas Avatar asked May 19 '26 01:05

Jim In Texas


1 Answers

My problem was really simple. Even though I had my TheApp class, I forgot to update the manifest such that TheApp would be called first. You do that with the 'name' application tag:

<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! -->

Now the app seems to work fine on or offline since setPersistenceEnabled(true) is actually being called. The only limitation is that you can't authenticate if you logged off and then lost your network. But it appears that if you were authenticated your app will work offline, even if you restart it. At least that appears to be the case.

like image 177
Jim In Texas Avatar answered May 21 '26 00:05

Jim In Texas



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!