Drew Szurko
Drew Szurko

Reputation: 1621

Android Firebase - Slow startup time when .setPersistenceEnabled(true)

I'm working on a simple APOD app where I display a RecyclerView of astronomy pictures that the user can scroll through. I enabled FirebaseDatabase.setPersistenceEnabled(true); so users can use the app offline and to reduce connections/download bandwidth. The problem is if I set FirebaseDatabase.setPersistenceEnabled(true);, my app startup time is significantly longer (6 seconds enabled vs 1.5 seconds disabled). I understand the first time a user launches the app the load time should be longer (because persistence is set up), but shouldn't every subsequent launch of the app be significantly shorter since it is not querying the online Firebase server?

To keep from loading many rows at once, I fire two SingleValueEventListeners, one after another. The first one loads 100 rows and loops through the databaseSnapshot using an AsyncTask, then displays the images. I then call the second SingleValueEventListener and loop through it the same way. The second SingleValueEventListener loads the remaining data, from 101 - 7000.

Below is my GlobalApp class that extends Application. This is where I enable persistence. The next 2 methods are where I load the data.

Extending Application Class

public class GlobalApp extends Application {

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

    FirebaseDatabase mDatabaseReference = FirebaseDatabase.getInstance();

    mDatabaseReference.setPersistenceEnabled(true);

    }
}

Initial Load

 private void initialLoad() {

    databaseReference.child("apod").orderByChild("date").limitToLast(100).addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {

            new LoadFirebase().execute(dataSnapshot);

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });

}

Second Load

private void secondLoad() {

   final String secondKey = firebaseKeys.get(firebaseKeys.size() - 1);

    databaseReference.child("apod").orderByChild("date").endAt(secondKey).addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(DataSnapshot dataSnapshot) {

            new SecondLoadFirebase().execute(dataSnapshot);

        }

        @Override
        public void onCancelled(DatabaseError databaseError) {

        }
    });

}

If I launch the app with FirebaseDatabase.setPersistenceEnabled(true);, my Logcat fills with:

04-18 18:22:22.649 2884-2915/com.foo.apod W/CursorWindow: Window is full: requested allocation 1344 bytes, free space 752 bytes, window size 2097152 bytes
04-18 18:22:22.733 2884-2915/com.foo.apod W/CursorWindow: Window is full: requested allocation 1821 bytes, free space 1124 bytes, window size 2097152 bytes
04-18 18:22:22.798 2884-2915/com.foo.apod W/CursorWindow: Window is full: requested allocation 1580 bytes, free space 1516 bytes, window size 2097152 bytes
04-18 18:22:22.868 2884-2915/com.foo.apod W/CursorWindow: Window is full: requested allocation 1574 bytes, free space 656 bytes, window size 2097152 bytes
04-18 18:22:22.920 2884-2915/com.foo.apod W/CursorWindow: Window is full: requested allocation 1455 bytes, free space 228 bytes, window size 2097152 bytes
04-18 18:22:22.973 2884-2915/com.foo.apod W/CursorWindow: Window is full: requested allocation 1579 bytes, free space 1000 bytes, window size 2097152 bytes
04-18 18:22:23.055 2884-2915/com.foo.apod W/CursorWindow: Window is full: requested allocation 1557 bytes, free space 756 bytes, window size 2097152 bytes
04-18 18:22:23.120 2884-2915/com.foo.apod W/CursorWindow: Window is full: requested allocation 1263 bytes, free space 620 bytes, window size 2097152 bytes

Followed by a bunch of GC calls:

04-18 18:22:26.290 2884-2891/com.foo.apod I/art: Background partial concurrent mark sweep GC freed 171401(23MB) AllocSpace objects, 0(0B) LOS objects, 15% free, 85MB/101MB, paused 2.120ms total 151.451ms
04-18 18:22:29.153 2884-2891/com.foo.apod I/art: Background partial concurrent mark sweep GC freed 552106(22MB) AllocSpace objects, 4(1096KB) LOS objects, 15% free, 84MB/100MB, paused 868us total 158.171ms
04-18 18:22:29.647 2884-2891/com.foo.apod I/art: Background sticky concurrent mark sweep GC freed 211953(18MB) AllocSpace objects, 0(0B) LOS objects, 14% free, 85MB/100MB, paused 2.590ms total 114.968ms
04-18 18:22:30.122 2884-2891/com.foo.apod I/art: Background sticky concurrent mark sweep GC freed 482294(17MB) AllocSpace objects, 43(3MB) LOS objects, 3% free, 96MB/100MB, paused 1.440ms total 100.242ms
04-18 18:22:31.091 2884-2906/com.foo.apod V/FA: Inactivity, disconnecting from the service

It seems the problem might be with SQLite since Firebase stores the data there. I added Stetho to my app and tried to view the database in the browser (thinking I was somehow creating duplicates with persistence enabled), but I could not access the database, it showed nothing.

Does anyone have a clue why this is happening? It took forever to find out where the issue was originating.

Please let me know if you need anymore code.

Thanks

Upvotes: 2

Views: 2009

Answers (2)

Kratos
Kratos

Reputation: 339

I had the same problem and after reading many documentations from firebase I understood that using :

AddSingleValueEventListener

while persistanceMode enabled will not work properly. AddSingleValueEventListener only listens to local memory not from database itself. And it doesnt update until you run:

AddValueEventListener or ChildEventListener

Do not use AddSingleValueEvenetListener. Use only :

 AddValueEventListener or ChildEventListener

if persistance mode is enabled

Upvotes: 0

Michael Lehenbauer
Michael Lehenbauer

Reputation: 16309

Unfortunately, Firebase Realtime Database does not implement property indexing of its offline queries. If you do a query at a location (/apod in your case), it will need to scan all of the cached data at that location in order to find the matching results, even if there are only a small number (100 or even 1) that match your query. So I think your slowness is likely due to scanning the 7000+ entries you have stored.

As a workaround, if you could organize your data such that you could read at a more granular location (e.g. /apod/2017/04/ with ~30 entries to query over), that would likely speed things up considerably.

Upvotes: 7

Related Questions