Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Firebase adding listeners in adapters in Android

I'm pretty new at firebase and I'm trying to wrap my head around it. I have this adapter that is registering to a ValueEventListener every time it is created. If I don't detach it, will the listeners add up as I rotate my phone and the adapter gets destroyed/rebuilt in the fragment?. Or is firebase smart enough to know that this particular listener already exists?

PS: I tried to unregister this listener in the onPause method of the fragment using it, but firebase seems to be removing my cache so after the fragment rotates it takes a while to fetch the data again, that didn't happen before.

like image 668
frankelot Avatar asked Apr 08 '15 22:04

frankelot


1 Answers

Good question. So, few things to note:

  1. Where are you attaching your listener? If you are attaching this anywhere but the onResume, it will re-initialize your listener. When setting a listener it fires all events for that particular node. However, I still do all my registering and unregistering to my Firebase reference in onPause, and onResume

  2. You can have multiple instances of any Firebase listener.

    Is firebase smart enough to know that this particular listener already exists?

    Firebase is aware that listener already exists and will not send the same event twice. However, when rotating you are creating a new instance of your listener. Firebase cannot see this as the same instanced listener. Therefore, you receive all data again.

  3. Firebase caches all of the data. When a fragment is attached and the listener is set, firebase will make two main calls -

    • First - A query to retrieve cached data.

    • Second - A query to the remote data.

    Calling cache first is nice because it still works in cases with slow to no network. Now, bear with me here... When Firebase receives that snapshot from the the online servers it will do a complex evaluation of the remote object and local object. And to the best of its ability, Firebase will merge the objects using a complex ID that utilizes timestamps and black magic [source needed]. With this new snapshot, if needed, it will save it to the servers. Then, **Firebase will provide the date to you only if it differs from the cached version and changes relative to the instance of the listener that provided said data. This cache-driven structure even applies to when you save your data:

    • First- save to cache.

    • Second- trigger callback.

    • Third- attempt to save to server.


To answer the question

If you are attaching your listener to Firebase onPause/onResume, you will receive all data again. The only way to not receive it again is to maintain the same instance of that listener.

In addition to maintaining my listener instance, I also have used another solution. In my opinion, I am not fond of it. But still is what I use most often. What I do, is

  • I will keep a final List<String>, called ignoredList. This list would be built of a String key, which would be the key for the object you already have in your adapter.

  • Then, in onPause I will add this data to my ignoredList and null out the childEvent listener.

  • After the onResume callback I set a new instance of childEvent listener.

  • On the onAdded of the event listener I check the newly added object against my list. If I have it, I will remove it from the list and nothing else. Essentially ignoring it. If the object is not in my ignoredList I will handle it like normal. If I receive it from one of the call backs other than onAdded, (i.e. onRemoved onChanged or onMoved) then I will make that event change to that object in the list and remove from ignoredList.

Now, I admit this is not really the prettiest solution. You could see incorrect data if two sources were modifying the same DataSnapshot. It would be a small chance, but entirely possible. Luckily if the data sets were to fall inaccurate, it will not save to Firebase.

I am always actively looking for better strategies for this, and if I find one I will share. In the meantime, this solution has been working perfectly for my apps.

like image 173
Chad Bingham Avatar answered Oct 20 '22 21:10

Chad Bingham