I have a question about Firebase Database. After data has been changed , does the listener give you whole data again or just the recently added data?
The documentation says:
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. The event callback
is passed a snapshot containing all data at that location, including
child data. If there is no data, the snapshot returned is null.
So also the unchanged parts of the data are returned.
This just means that you should use as specific and narrowed-down listeners as possible, as the documentation also suggests:
Important: The onDataChange() method is called every time data is
changed at the specified database reference, including changes to
children. To limit the size of your snapshots, attach only at the
highest level needed for watching changes. For example, attaching a
listener to the root of your database is not recommended.
Related
Scenario
I am using onSnapshot() to listen to document changes in Firestore. My document contains an array field. I would like to see which items were added/deleted from the nested array in the document. Is there any way to achieve this?
Possible Solution
I see there is an oldDocuments() field in the QuerySnapshot.snapshot object which could be used to achieve my goal. I could just compare the oldDocument with the returned snapshot to see what changed in the nested array. The problem is oldDocument is a private field that I can't access through the API.
I am using Android Kotlin as my client.
There is nothing built in that can help you see what exactly in a document has changed. However, there are two workarounds.
The first one would be to attach a persistent listener and view changes between snapshots. That means that when you attach the listener you get a list of all documents your query returns. As soon as a document is changed, onEvent will fire. This means that you'll be able to know the document that has changed. Knowing that you already have a list with the initial documents, you can find the old document based on the ID and compare it against the new one.
Another solution would be to save a new document each time something changes. This is some kind of document versioning. In this way, you'll always be able to check the new document against the old one using only two document reads.
In an effort to reduce data transfer (since it's costing us a lot of money), we're in the process of updating our Firebase Realtime Database value listener to use child listeners. Since most of the fields don't change, and the ones that change most frequently we only care about in certain app states, using only child added and child removed listeners seems to fit the bill.
Adding listeners for just child_added and child_removed appears to work perfectly on web. And iOS seems to be set up that way as well, though I admittedly haven't tested there yet. Android, however, appears to require that we add one massive listener that will listen for child moved and child changed too.
Listening for these, to my understanding, mostly defeats the purpose of using the child listeners over the value listener. Since any time any of the child's data changes (most of which we don't care about most of the time), it will send the entire child's snapshot again. We're trying to avoid data transfer by only listening for added/removed and circumstantially specific child properties, but this requires we basically listen to all of it at all times? Is there another way to implement this, to get what I'm hoping for?
To summarize our object, we have:
parent
--child A
----child A property 1
----child A property 2
--child B
----child B property 1
----child B property 2
etc. And we want to know when a child is added or removed, but most of the time we don't care about keeping the child's properties updated and would prefer to avoid re-transferring that data. How is that accomplished on Android (using Java, specifically)? Thanks!
There is no difference between the data transferred for a ValueEventListener vs a ChildEventListener when they are used on the same query/path in the database.
If you only want to know when a child is added/remove, but not about their contents, consider adding an additional top-level branch to your tree, where you keep just the key of the child node and then true as its value.
I have read through the android documentation in https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo.html
I don't understand the description stated in the document about recycle() and refresh() method.
1. recycle() - Return an instance back to be reused.
The instance is return back to where?
In which scenario this instance going to be reused?
AccessibilityNodeInfo might contains child node, do i need to call recycle() when my code navigate to each of the node or just call recycle method at root node?
2. refresh() - Refreshes this info with the latest state of the view it represents
I thought when onAccessibilityEvent() method was called, the AccessibilityEvent object should contain the latest state?
AccessibilityNodeInfo might contains child node, do i need to call refresh() when my code navigate to each of the node or just call refresh method at root node?
The Android Accessibility API uses a pool of AccessibilityNodeInfo nodes. That way, iterating over large trees will not create many objects that would slow down the garbage collector. In other words, when you recycle() a node, you might later (on the next event for example, or when iterating over the same tree) receive the exact same node object again, but filled with completely different details. That's why it is important you don't hold references to nodes you have recycled (and for example try to compare them to other nodes).
When you obtain child nodes, you have to recycle each child node. When you do not get them, you do not need to recycle them. You can recycle children before you recycle parents, or the other way round, depending on how long you will need access to the objects.
When you receive a node, it contains the latest state. But when you perform an action on it (e.g. click or scroll), the state of the node or of other nodes may change. If you want to see these changes in real-time (and not only when you receive the next event), you have to refresh() the node (or you can refresh() the root and try to obtain a new copy of the node from the root)
When you freshly obtain child nodes, you do not need to refresh them (they are already fresh). You only need to refresh nodes that you obtained earlier (before doing some interaction with them or with other nodes).
recycle()
In my (relatively brief) usage of an accessibility service, it seems you are not expected to call recycle(), or at least not on nodes you have not explicitly requested (e.g. through one of the functions mentioned below); I have received some fairly arbitrary crashes from this due to the backing cache (in the SDK) clearing nodes that it itself was holding on to (!) and I've looked at the source code and it appears all called nodes are added to a backing cache that later gets cleared. There are few ways to safely check if a node was already recycled and the SDK code definitely does not.
By holding on to nodes I have found that they eventually get recycled by some SDK code, too, thus my observation that it may not be expected for developers to do it themselves.
refresh()
At the time you receive a node, it is accurate. If you perform any actions that give you another node (getChild(i), findAccessibilityNodeInfosByViewId(viewId), etc.), the SDK calls a function to retrieve fresh info -- so it will be accurate at the time.
It seems to be relatively safe to hold on to nodes so long as you check their validity; However, most fields nevermind functions will throw an exception if the node has already been recycled somewhere. So: I would recommend you create yourself a convenience isValid() function to check this on all nodes.
Just check that it isn't null (some events will give you a null source!) and that the className attribute is not null (always safe to check, and is nulled when unused or recycled).
I have a Fragment, and once the user presses OK, an Item is added to my database and its ID is added to the ArrayAdapter. Immediately after, the adapter tries to draw the view, but the first time it tries to get its attributes, it returns a null HashMap (it gets drawn properly the following times).
Is there a way to make sure the item is in the table before trying to get its attributes?
Even putting the attribute retrieval into a while loop until it returns a not-null HashMap doesn't work so it doesn't look to be an issue of time.
You need to do Select or GetAttributes with ConsistentRead=true as Amazon SimpleDB supports two read consistency options: eventually consistent read and consistent read. Eventually consistent read is default. For more detail please refer doc. link
Try using AsynTask.
Add item to database in doInBackground.
Read it in postExecute.
You are done.
somehow I don't understand the working concept of the query parameter CALLER_IS_SYNCADAPTER. Its default value is false, if set, the DIRTY flag is not automatically set. So what does it actually mean? Out from my understanding, each change on a contact results in setting the dirty flag to 1. After a sync adapter finished the job, using insert/update/delete with the CALLER_IS_SYNCADAPTER the inserted/updated and deleted records should have a dirty flag of 0, is that right?
However if I invoke queries with that optional parameter, the entries remain with the flag 1.
Is there something else I have to do, or is my understanding how it should work wrong? Or is there something to tell the system the sync has been finished successfully to set the flags?
Does anybody have a sample or some advices for further reading?
CALLER_IS_SYNCADAPTER doesn't necessarily affect what's stored in the database row, it depends on the command performed. It shouldn't have an effect on queries. Do not use it from a user application on the device.
Now... Why does it exist?
It is provided to help with notifyChange() / ContentObservers / ContentResolver / Syncadapter integration. There are two use cases for changing a row in the database.
Local user edits from an application.
Changes come from the network (via SyncAdapter)
Either change requires the UI to update, if it's onscreen. Therefore ContentResolver.notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) gets called. This updates ContentObservers and tells them to go fetch the newest data from the ContentProvider DB. That last parameter in the call is your clue.
ContentResolver itself is a ContentObserver. When it sees the database change, it considers starting up your SyncAdapter to push the change up to the network. This is great in case 1. In case 2, it's redundant. The change came from the network, there's no reason at all to start up a sync to send the change back.
Calendar.CALLER_IS_SYNCADAPTER is a cue used within the update() performed by the SyncAdapter. When it's true, ContentProvider sets syncToNetwork as false, ensuring a redundant second sync is not performed
A second example is as veljko mentioned. The cleanest way to delete a thing from the server is to set the delete flag, and then perform a sync. When the CALLER_IS_SYNCADAPTER flag is false (user app) a call to delete() sets the flag. When the flag is true (sync is happening), a call to delete() pushes the deletion up to the server and removes the row from the local DB. There's only one delete() call, this flag allows the ContentProvider to know which task it's supposed to do.
You can add to your existing Uri:
myUri=calendarUri.buildUpon().appendQueryParameter(Calendar.CALLER_IS_SYNCADAPTER, "true").build();
Here is from Javadoc:
/**
* An optional insert, update or delete URI parameter that allows the caller
* to specify that it is a sync adapter. The default value is false. If true
* the dirty flag is not automatically set and the "syncToNetwork" parameter
* is set to false when calling
* {#link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}.
*/
.
The invocation of resolver.delete(...), does not immediately delete a raw contacts row. Instead, it sets the DELETED flag on the raw contact and removes the raw contact from its aggregate contact. The sync adapter then deletes the raw contact from the server and finalizes phone-side deletion by calling resolver.delete(...) again and passing the CALLER_IS_SYNCADAPTER query parameter.