38

I’m creating my first Firebase App. One of its requirements is that it run when the network is not available. The Firebase guide states:

Enabling disk persistence allows our app to also keep all of its state even after an app restart. We can enable disk persistence with just one line of code. FirebaseDatabase.getInstance().setPersistenceEnabled(true); With disk persistence enabled, our synced data and writes will be persisted to disk across app restarts and our app should work seamlessly in offline situations.

Another requirement is to use Google Sign In. So in my MainActivity I check if the User is signed in, if not, I launch the SignInActivity. (The SignInActivity is from the Firebase examples.) The SignInActivity works, the user gets logged in, and MainActivity is launched for a second time. Now my app crashes on the code line FirebaseDatabase.getInstance().setPersistenceEnabled(true); with the following message:

Calls to setPersistenceEnabled() must be made before any other usage of FirebaseDatabase instance.

Now if I restart my app, the User is signed in, the SignInActivity is not launched, my app runs fine.

Any suggestions of how I avoid this crash after the User signs in?

As I was posting this question, I received a suggestion to relocate FirebaseDatabase.getInstance().setPersistenceEnabled(true); to my “Application class”. I get exactly the same result … SignInActivity starts, completes, and I get a crash on the setPersistenceEnabled.

Below is my MainActivity onCreate:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // Calls to setPersistenceEnabled() must be made before any other usage of FirebaseDatabase instance.
    // Crash here upon returning from SignInActivity.  
    FirebaseDatabase.getInstance().setPersistenceEnabled(true);
    mFirebaseDbReference = FirebaseDatabase.getInstance().getReference();

    // Initialize Firebase Auth
    mFirebaseAuth = FirebaseAuth.getInstance();
    mFirebaseUser = mFirebaseAuth.getCurrentUser();
    if (mFirebaseUser == null) {
        // Not signed in, launch the Sign In activity
        Timber.tag("MainActivity").i("onCreate(): User not signed in, launching SignInActivity");
        startActivity(new Intent(this, SignInActivity.class));
        finish();

    } else {
        mUsername = mFirebaseUser.getDisplayName();
        Timber.tag("MainActivity").i("onCreate(): User \"%s\" signed in.", mUsername);
        if (mFirebaseUser.getPhotoUrl() != null) {
            mPhotoUrl = mFirebaseUser.getPhotoUrl().toString();
        }
    } 
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Loren
  • 820
  • 2
  • 11
  • 18

17 Answers17

54

A FirebaseApp is initialized by a ContentProvider so it is not initialized at the time onCreate() is called.

Get your FirebaseDatabase like this:

public class Utils {
    private static FirebaseDatabase mDatabase;

    public static FirebaseDatabase getDatabase() {
       if (mDatabase == null) {
          mDatabase = FirebaseDatabase.getInstance();
          mDatabase.setPersistenceEnabled(true);
       }
       return mDatabase;
    }

}

Then call Utils.getDatabase() from any activity you want.

Read more in this article

Dan Alboteanu
  • 9,404
  • 1
  • 52
  • 40
52

I fixed this exception by using setPersistenceEnabled(true) in my Application class.

public class MApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        FirebaseDatabase.getInstance().setPersistenceEnabled(true);
    }
}

In AndroidManifest.xml, set the application name as MApplication:

<application
    android:name=".MApplication"
    ... />
dknchris
  • 487
  • 9
  • 17
27

I was facing a similar problem and using a static variable seemed to resolve the issue for me. So at first my code looked something like this

@Override
protected void onCreate(Bundle savedInstanceState) {
    //..code

    FirebaseDatabase.getInstance().setPersistenceEnabled(true);
    FirebaseDatabase database = FirebaseDatabase.getInstance();

    //..code
}

and now it looks more like

static boolean calledAlready = false;

@Override
protected void onCreate(Bundle savedInstanceState) {
    //..code

    if (!calledAlready)
    {
        FirebaseDatabase.getInstance().setPersistenceEnabled(true);
        calledAlready = true;
    }

    FirebaseDatabase database = FirebaseDatabase.getInstance();

    //..code
}

Hope it helps!

businesscasual
  • 755
  • 9
  • 14
19

I bit late but today i got this issue, I solved by adding

static {
    FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}

to my Activity

rkmax
  • 17,633
  • 23
  • 91
  • 176
3

To me, it is easier to be handled by creating a separate class for Firebase. This is because Firebase has its own instance and if you are using it in more than one activity, there's the possibility for it to crash if you call setPersistenceEnabled again in another activity.

Another good thing is that you can pass your context or parameters into the FirebaseHandler constructor if required. Or if you have fixed location in the database, they can be called easy without the .child("location") boilerplate.

Example:

public class FirebaseHandler {

    // parameters
    private Context context;
    private String userKey;
    private DatabaseReference databaseReference;
    private static boolean isPersistenceEnabled = false;
    private static String fixedLocationA = "locationA";
    private static String fixedLocationB = "locationB";

    public FirebaseHandler(Context context, String userKey) {
        this.context = context;    // context can be used to call PreferenceManager etc.
        this.userKey = userKey;
        if (!isPersistenceEnabled) {
            FirebaseDatabase.getInstance().setPersistenceEnabled(true);
            isPersistenceEnabled = true;
        }
        databaseReference = FirebaseDatabase.getInstance().getReference().child(userKey);
    }

    public DatabaseReference getRefA() {
        return databaseReference.child(fixedLocationA);
    }

    public DatabaseReference getRefB() {
        return databaseReference.child(fixedLocationB);
    }
}

This can then be called in any Activity as below.

public class MyActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // get instance
        FirebaseHandler firebaseHandler = new FirebaseHander(this, "userKey");
        // to set value
        firebaseHandler.getRefA().setValue("value");

        // to set listener
        firebaseHandler.getRefB().addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                // TODO here....

                // also, can remove listener if required
                if (certain condition) {
                    firebaseHandler.getRefB().removeEventListener(this);
                }
            }
        }
    }
}
tingyik90
  • 1,641
  • 14
  • 23
1

I'm facing some problem too, but this is my temporary solution for my app.

Create BaseActivity extends AppcompatActivity and override onCreate, put setPersistenceEnabled there.

public class BaseActivity extends AppCompatActivity {

    private static String TAG = "BaseActivity";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        try{
            FirebaseDatabase.getInstance().setPersistenceEnabled(true);
            Log.d(TAG,FirebaseDatabase.getInstance().toString());
        }catch (Exception e){
            Log.w(TAG,"SetPresistenceEnabled:Fail"+FirebaseDatabase.getInstance().toString());
            e.printStackTrace();
        }
    }
}

And change MainActivity to extend BaseActivity

public class MainActivity extends BaseActivity

EDIT: Follow @imakeApps answer

public class BaseActivity extends AppCompatActivity {

    private static String TAG = "BaseActivity";

    static boolean isInitialized = false;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        try{
            if(!isInitialized){
                FirebaseDatabase.getInstance().setPersistenceEnabled(true);
                isInitialized = true;
            }else {
                Log.d(TAG,"Already Initialized");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
Sucipto
  • 817
  • 1
  • 8
  • 19
  • Thanks Chip ... the only place where setPersistenceEnabled exists is currently in my MainActivity it is not called in my SignInActivity. – Loren May 27 '16 at 17:31
  • Chip ... your suggestion to surround the setPersistenceEnable with a try/catch and then just log the error and continue. Have you verified whether in fact data is persisted locally? – Loren May 27 '16 at 17:33
  • @Loren my app crash only when i close and open again, i think it's still working offline, because i try to enable airplane mode and my data stil exists on my app. – Sucipto May 28 '16 at 05:12
1

Solved it by making the Firebase reference a static class field like this:

public class MainActivity extends AppCompatActivity

private static FirebaseDatabase fbDatabase;

@Override
    protected void onCreate(Bundle savedInstanceState) {

if(fbDatabase == null) {
            fbDatabase = FirebaseDatabase.getInstance();
            fbDatabase.setPersistenceEnabled(true);
        }

It's no problem to create new Firebase references (without setPersistenceEnabled(true)) in other activities too.

Helmwag
  • 460
  • 5
  • 7
1

If you don't like the static fields, this did the trick for me:

if (FirebaseApp.getApps(context).isEmpty()) {
     FirebaseApp.initializeApp(context);
     FirebaseDatabase.getInstance().setPersistenceEnabled(true);
}

This can be caused be more that one process initializing twice firebase or Multidex apps. For more info see this: https://github.com/firebase/quickstart-android/issues/15

Yair Kukielka
  • 10,686
  • 1
  • 38
  • 46
1

I wouldn't recommend using Application to store the data because like its written in CodePath

There is always data and information that is needed in many places within your app. This might be a session token, the result of an expensive computation, etc. It might be tempting to use the application instance in order to avoid the overhead of passing objects between activities or keeping those in persistent storage.

However, you should never store mutable instance data inside the Application object because if you assume that your data will stay there, your application will inevitably crash at some point with a NullPointerException. The application object is not guaranteed to stay in memory forever, it will get killed. Contrary to popular belief, the app won’t be restarted from scratch. Android will create a new Application object and start the activity where the user was before to give the illusion that the application was never killed in the first place.

Thats the reason I would recommend using a Singleton like this:

public class DataBaseUtil {

private static FirebaseDatabase mDatabase;

public static FirebaseDatabase getDatabase() {
    if (mDatabase == null) {
        mDatabase = FirebaseDatabase.getInstance();
        mDatabase.setPersistenceEnabled(true);
    }
    return mDatabase;
}}

just use it in your code then like

private FirebaseDatabase fdb = DataBaseUtil.getDatabase();
Community
  • 1
  • 1
Musab Kurt
  • 85
  • 2
  • 10
1

Create a class called Util.java

and add following code

public class Util {

        private static FirebaseDatabase mData;

        public static FirebaseDatabase getDatabase() {
            if (mData == null) {

                mData = FirebaseDatabase.getInstance();
                mData.setPersistenceEnabled(true);
            }
            return mData;
        }


}

Now replace FirebaseDatabase.getIntance() with Util.getDatabase() every time in each activity. Calling just once will get the error!

0

I was facing same issue. i changed code as below.

BEFORE(Causing Crash)

    var rootRef = FIRDatabase.database().reference()

    override func viewDidLoad() {
       super.viewDidLoad()
       FIRDatabase.database().persistenceEnabled = true
    }

AFTER (Resolved Crash)

var rootRef:FIRDatabaseReference!
override func viewDidLoad() {
 super.viewDidLoad()
   FIRDatabase.database().persistenceEnabled = true
   rootRef = FIRDatabase.database().reference()
}
Dattatray Deokar
  • 1,923
  • 2
  • 21
  • 31
0

In Menifest

 android:name=".AppName

Create java Class that extends Application

public class AppName extends Application {

@Override
public void onCreate() {
    super.onCreate();
    FirebaseDatabase.getInstance().setPersistenceEnabled(true);

}
0

Make sure that .setpersistenceenabled(true) is not happening twice, while sign in by Google in your case, second care setPersistenceEnabled(true) is must be called before any instance of firebase called this resolve my issue.

Sachin Keche
  • 43
  • 3
  • 12
0

For Kotlin Try this:

class DatabaseUtil {

    companion object {
        private val firebaseDatabase: FirebaseDatabase = FirebaseDatabase.getInstance()

        init {
            firebaseDatabase.setPersistenceEnabled(true)
        }

        fun getDatabase() : FirebaseDatabase {
            return firebaseDatabase
        }
    }
}
0

Simply move you code in ExampleFragment.class from onCreateView method to onCreate method:

 @Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    FirebaseDatabase.getInstance().setPersistenceEnabled(true);

}

@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                         @Nullable Bundle savedInstanceState) {

     ....

    // Inflate the layout for this fragment

    View view =  inflater.inflate(R.layout.fragment_home, container, false);
azamatikus
  • 73
  • 6
-1

The error message describes the problem:

Calls to setPersistenceEnabled() must be made before any other usage of FirebaseDatabase instance.

The fix to this problem is described into the documentation: As in SDK 2.x, persistence of the disk must be enabled before other calls to the database are made.

FirebaseDatabase.getInstance().setPersistenceEnabled(true);

https://firebase.google.com/support/guides/firebase-android

Jorgesys
  • 124,308
  • 23
  • 334
  • 268
-2

You can use:

FirebaseDatabase.getInstance().setPersistenceEnabled(true);

before using FirebaseDatabase.getInstance().getReference();

Shaishav Jogani
  • 2,111
  • 3
  • 23
  • 33
  • It seems like `setPersistenceEnabled` is already called before `FirebaseDatabase.getInstance().getReference()` in the original question's code... am I misunderstanding your answer? – Joshua Grosso Reinstate CMs Jun 19 '17 at 20:45