I have an android app that uses Firebase for the backend. In my database, I have a section called USER_NOTIFICATIONS which gets populated with some information when user's post gets liked.
Now, in the app, should I have a ChildEventListener that gets triggered any time a notification happens or should I have a SingleValueEventListener inside a function and call it every 20 seconds inside a Runnable? Is it expensive to run SingleEventListener in a Runnable?
The majority of the resource usage of a listener comes from the data it reads. When you attach a listener to a location, it reads the data from that location and fires the relevant events.
If you use addListenerForSingleValueEvent that's all it does. But if you use addValueEventListener or addChildEventListener, the listener stays active and will also be called if the data is updated afterwards.
a listener on a piece of data that never changes is quite cheap. But keeping a listener on data that changes frequently is as expensive as the size of the data changes.
In other words: there is no way to tell what to do without knowing all of the above. Without knowing those, I'd go for the simplest possible approach, put some measurements in place, and optimize as you discover more about your app's behavior.
I'm assuming you want to your app to respond to changes in the database, in which case you should use ChildEventListener. You can specify different behavior for 4 actions (onChildAdded, onChildChanged, onChildMoved and onChildRemoved).
Related
I am very new to Cloud Firestore and have some doubts regarding the querylistener read count. As most of the answer suggested on stack overflow that, we should attach the listener in onStart() and detach in OnStop() in the Android activity. I am building an app in which I have to attach the listener on Dashboard Activity and the user will navigate back and forth between dashboard and other activity frequently.
So,
when the user navigates to another activity the listener will be detached (onStop()) and if they come back it will be attached again (onStart()). And suppose between this, nothing has changed in documents on the server, will I still be charged for all read count even though nothing has changed on the server?
Should I keep the listener attach and detach only when the user kills the app? By doing this, I will be only charged for the document which is changed while the app is in the foreground and not the all-read count if navigates between Dashboard and other activity.
we should attach the listener in onStart() and detach in OnStop() in the Android activity
If you are attaching a listener for getting real-time updates, yes, you need to remove the listener according to the life-cycle of the activity, as explained in my answer from the following post:
How to set addSnapshotListener and remove in populateViewHolder in RecyclerView Item?
However, if you simply create a get() call, followed by addOnCompleteListener(), there is no listener that needs to be removed.
And to answer your first questions. It depends. If you are using a SnapshotListener and nothing is changed on the server, you'll always read the data from the cache. So you can go back and forth, as long as you want. But also keep in mind, that if the listener is disconnected for more than 30 minutes, you’ll be charged for reads as if we had issued a brand-new query. On the other hand, if you are using get(), you'll be charged with a number of read operation that is equal to the number of documents that you get, every time you call it, even if there is nothing changed on the server.
Why?
In order to provide up-to-date data, the Firestore SDK needs to check the online version of the documents against the cached one. That’s the reason why you are charged, regardless of what exists in the cache or if something is changed or not on the server.
To answer your second question, if you are using a SnapshotListener, you should always consider removing the listener, otherwise, your app will remain synchronized until Android OS will eventually close the app. It doesn't really matter if the app is in the foreground or in the background, if the listener stays active, you'll always be charged accordingly. You can find more info in the following article:
How to drastically reduce the number of reads when no documents are changed in Firestore?
I want to save whole Firebase data to device.
ValueListeners and ChildListeners have to be called a lot of times in the app. This becomes a little cumbersome. Sometimes it makes the app a little slow.
I know we can use setPersistenceEnabled, but in that case Listeners have to be called.
So, is there any way, in which we can save data (database) in device in a way, from which accessing the data can be fast and easy?
The only way to get data from the server onto your device is by attaching a listener. But a convenient way to attach a listener without coding it yourself, is by calling keepSynced(true) on a reference or query. The SDK in that case attaches an empty listener to the node(s), so that the data in the cache stays up to date.
I am using Firestore for Android. I know it has persistence enabled by default.
Here is some background:
My app shows a list of notes and every note is tied to a label. Therefore while creating a note, I must present a list of labels (in a Dialog, in response to 'Select label' button), which will be provided to me by a snapshot listener on a Query. As creating a note is a separate Activity, each time user goes to create one, the complete list of labels must be presented.
My questions are:
Is snapshot listener smart enough to fetch the data from cache every other time, except for the first time? (And that "first time" will be after 30 mins when the listener expires, or Firestore clears the cache to save space, right?)
What is the impact of attaching and detaching listeners frequently? I am attaching the listener in Activity's onStart() and removing it onStop(). Here, the user may switch between apps to copy data from some other source to add it in the note, making listener detach/attach. Will this impact my read count?
How get() will behave in these scenarios? (I am not a fan of this as it isn't realtime)
Firestore keeps confusing me about the pricing as I dive deep into it. Need a good clarity on the behaviour of components around offline data and its pricing side.
Is snapshot listener smart enough to fetch the data from cache every other time, except for the first time? (And that "first time" will be after 30 mins when the listener expires, or Firestore clears the cache to save space, right?)
If you are offline, yes, Firestore will get all the data from the cache. This is happening when you are listening to real-time updates. On the other hand, if the real-time updates are not mandatory, you can simply use a get() call and specify the source, as explained in my answer from the following post:
How to include Source Cache in cloud firestore realtime update in MVVM architecture android
Regarding the cache limit, please check out the following answer:
What is offline data saving limit of Firestore?
What is the impact of attaching and detaching listener frequently? I am attaching the listener in Activity's onStart() and removing it onStop(). Here, user may switch between apps to copy data from some other source to add it in the note, making listener detach/attach. Will this impact my read count?
The listeners in Cloud Firestore are cheap, and you should not worry about lots of listeners attached to a document. Attaching and detaching the listeners is the way to go ahead with. It's mandatory to detach the listeners before the activity gets destroyed, as explained in my answer from the following post:
How to set addSnapshotListener and remove in populateViewHolder in RecyclerView Item?
How get() will behave in these scenarios? (I am not a fan of this as it isn't realtime)
When you are using get(), it means that you are getting the data only once. It's the correspondent of addListenerForSingleValueEvent() from Firebase Realtime Database.
I know how I can add listeners on database references to detect when there's a change in the data and retrieve that data. But I can't seem to find out how I can get data from firebase when the app is first installed on the user's device. Its not going to happen that the user's installs the app and the data is going to change. How do I get the data without triggering the listeners for this scenario?
Listeners are triggered once when the listener is attached, and once when the relevant event happens.
So, just by creating a ValueEventListener, you'll get a snapshot of all its current data (and you'll get it every time you attach, not just once after you install the app).
There's more in the documentation about how to listen for value events, but the specifically relevant text is:
You can use the onDataChange() method to read a static snapshot of the contents at a given path, as they existed at the time of the event. This method is triggered once when the listener is attached and again every time the data, including children, changes.
This is also true for ChildEventListeners, via the onChildAdded method. It is called for all items that exist, then once each time an item is actually added. See here.
I'd like to read all children of a node and then keep track of any changes that happen after that. One use case might be a local cache where I want to hold off using the cache until I know it's populated.
The one way I could imagine doing that is by first calling Query.addListenerForSingleValueEvent() to get the initial state. After I get the result, I know my local cache is populated, and I can now call Query.addChildEventListener() to get updates on when children are added and removed.
However, this seems inefficient - now the entire list of children is being sent across the wire twice, and I need to add a check to skip entries coming from addChildEventListener() that I already got from addListenerForSingleValueEvent().
It's not that big of a deal, but I wonder if there's a better way - isn't there a way to simply call addChildEventListener() and get a signal when all entries have been sent?
To both start synchronizing data and know when the initial synchronization has finished, you will need to add two listeners.
a ChildEventListener to handle the actual updates.
a ValueEventListener to detect when a complete update has been sent.
For the initial data from the database, your ChildEventListener.onChildAdded will be called first for each child node. After that the ValueEventListener.onDataChange will be called.
So as soon as onDataChange gets called, you can know that all initial data has loaded. If you only care about this initial data being complete, you can register this listener with addSingleValueEventListener so that it detaches after the initial synchronization.
Note that Firebase transfers data only once, even when there are multiple listeners for that data. So if you attach both listeners to the same location, the data will only be read once.