0

I have set up a realtime Firebase-Database and it communicates without a glitch with the android app I am building … until I add db.setPersistenceEnabled(true); (db being the instantiated global FirebaseDatabase variable) and do an orientation change.

Researching on StackOverflow and the documentation I saw a few cases where this was solved by using an if-clause to ensure the code does not retry to persist. But even with that in place, the app crashes.

I am suspecting that the ValueEventListener is somehow playing a role in this. I have tried to add and remove the listener in onStart() and onStop() but that too did not prevent the crash.

Here is my current version of the code (having removed all the above-mentioned trials so as not to add confusion):

    private FirebaseDatabase db;
    private DatabaseReference dbRef;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        db = FirebaseDatabase.getInstance();
            db.setPersistenceEnabled(true);
            isPersistenceEnabled = true;
        dbRef = db.getReference("meditations");
        dbRef.keepSynced(true);

        dbRef.addValueEventListener(new ValueEventListener() {
                @Override
                public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
                    meditations.clear();
                    List<String> keys = new ArrayList<>();

                    for (DataSnapshot keyNodes : dataSnapshot.getChildren()) {
                        keys.add(keyNodes.getKey());
                        Meditation meditation = keyNodes.getValue(Meditation.class);
                        meditations.add(meditation);
                        Log.v(getClass().getSimpleName(), "medtiation added");
                    }
                }

                @Override
                public void onCancelled(@NonNull DatabaseError databaseError) {

                }
            });
    }

Does anyone have experience with this scenario? Am I overlooking something?

ETA: Adding stacktrace for the full picture (in case others have a similar problem):

2

019-05-14 15:29:31.175 16705-16705/com.example.android.meditationhub E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.android.meditationhub, PID: 16705
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.android.meditationhub/com.example.android.meditationhub.ui.MainActivity}: com.google.firebase.database.DatabaseException: Calls to setPersistenceEnabled() must be made before any other usage of FirebaseDatabase instance.
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2724)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2789)
        at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4621)
        at android.app.ActivityThread.-wrap19(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1533)
        at android.os.Handler.dispatchMessage(Handler.java:110)
        at android.os.Looper.loop(Looper.java:203)
        at android.app.ActivityThread.main(ActivityThread.java:6251)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924)
     Caused by: com.google.firebase.database.DatabaseException: Calls to setPersistenceEnabled() must be made before any other usage of FirebaseDatabase instance.
        at com.google.firebase.database.FirebaseDatabase.assertUnfrozen(com.google.firebase:firebase-database@@17.0.0:316)
        at com.google.firebase.database.FirebaseDatabase.setPersistenceEnabled(com.google.firebase:firebase-database@@17.0.0:284)
        at com.example.android.meditationhub.ui.MainActivity.onCreate(MainActivity.java:46)
        at android.app.Activity.performCreate(Activity.java:6725)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1118)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2677)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2789) 
        at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:4621) 
        at android.app.ActivityThread.-wrap19(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1533) 
        at android.os.Handler.dispatchMessage(Handler.java:110) 
        at android.os.Looper.loop(Looper.java:203) 
        at android.app.ActivityThread.main(ActivityThread.java:6251) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1063) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:924) 

ETA2: I see this has been marked as a duplicate question. I had gone over the solutions provided for the question linked and none worked in my case (as I stated above).

atschpe
  • 121
  • 14
  • Just try to add in manifest file in your `` `android:configChanges="keyboardHidden|orientation|screenSize"`. – Nick Bapu May 14 '19 at 13:15
  • Consider using `LiveData` to prevent data loss during configuration changes. Live Data works well with firebase. (I use in my app) – Abhimanyu May 14 '19 at 13:16
  • Thanks, @NickBapu, That worked. I hadn't thought of looking there. Do you want to pop in the answer so I can accept it? – atschpe May 14 '19 at 13:18
  • @Abhi, thanks. I removed LiveData for a bit to figure out why this was acting up. I'll be reapplying it now it works again. – atschpe May 14 '19 at 13:20
  • @AlexMamo. Thanks for the reminder. I was about to do that, but Nick Bapu beat me to it by immediately pointing out what I had overlooked. – atschpe May 14 '19 at 13:22

1 Answers1

1

Because you need tell your widgets just stay as it is when orientation changed. So, you need to put one line in your <activity> in AndroidManifest.xml file:

<activity
        android:name="NAME_OF_YOUR_ACTIVITY"
        android:configChanges="keyboardHidden|orientation|screenSize" />
Nick Bapu
  • 463
  • 1
  • 4
  • 14
  • The correct answer is to create a different layout file for landscape. Then store the key-values in `Bundle` inside `onSaveInstanceState()` and get the values back in `onRestoreInstanceState()` – HB. May 14 '19 at 14:08
  • @atschpe Here is a good article - https://medium.com/@doyouseeitmyway/save-and-restore-instance-state-made-easy-cf6f175f54b0 – HB. May 14 '19 at 14:17
  • You almost certainly don't want to do this in your activity. It's a long-standing anti-pattern in Android development. You're must better off handling the activity lifecycle yourself correctly by setting up and shutting down listeners along with the lifecycle. – Doug Stevenson May 14 '19 at 14:23
  • @DougStevenson, could you explain how to do handle the lifecycle correctly for this case? I tried to add and remove the Listener in onStart and onStop (thinking that it together with the Persistence is causing the issue) but to no avail. Should also add and remove persistence there? – atschpe May 15 '19 at 11:45
  • @HB. I know of onSavedInstanceStet but am used to using it on input-variables and similar. How would this be used for a database and/or listener? Have not seen that done there so far. – atschpe May 15 '19 at 11:46
  • You can store your `ArrayList<>` in Bundle inside `onSaveInstanceState()`. Then in `onCreate` check if your key-value is empty, if it is then you haven't saved your Array and you need to fetch the data from `FirebaseDatabase` - this will only happen once. In `onRestoreInstanceState` you get the `ArrayList<>` back by checking if the key-value is empty or not and use it accordingly. – HB. May 16 '19 at 03:26
  • By doing it this way, you will only call `FirebaseDatabase` once, in return, the error will be gone. – HB. May 16 '19 at 03:28