is it possible to get an event in a Listener when I replace value of a node with the same value? seams like if firebase notice that the value is the same no event accures..:s
The Firebase Database synchronizes state between clients. If a write operation doesn't change the state, there is nothing to synchronize to the other listening clients.
In simple code:
ref.setValue(1);
ref.setValue(1); // won't trigger listeners
ref.setValue(2); // will trigger active listeners
ref.setValue(2); // won't trigger listeners
It sounds like you want to instead pass messages between clients. This is totally feasible, but means that you should model your data differently. Instead of storing the value in your database, store the fact that the client wrote a value.
In simple code:
ref.push(1);
ref.push(1);
ref.push(2);
ref.push(2);
In this last sample each write will trigger listeners on ref, since each write is new.
This is a common pattern: instead of storing the final state, you're storing the state changes. It essentially similar the mechanism behind oplogs in databases and many other systems.
Because nothing happens there is no event that is triggered. What can you do in stead, is to check the value before you are setting it.
if(myValue.equals(firebaseValue)) {
//do something
} else {
//do something else
}
Hope it helps.
Related
I need some clear explanation with addChildEventListener and Query event binding.
I use firebase in my android app. In the onCreate() method of my activity, I create something like this...
fiDbQuery = fiDbRef.child("users").orderByChild("name");
fiDbQuery.addChildEventListener(// listener //);
... where fiDbQuery is a Query instance in the activity (global var) and fiDbRef is a DatabaseReference instance in the activity too, where I already set the initial value.
all is well, when activity first loaded, all the users data are loaded and shown. But then, I have some button to sort this data according to users field, either by email, age, name, etc.
and in those buttons onClickevent listener, I do something like this... (this one is the sort by age button)
fiDbQuery = fiDbRef.child("users").orderByChild("age");
... and I didn't provide the addChildEventListener function because it is already "added" on onCreate() method, right? The data aren't loaded, but if I add the addChildEventListener function it works again.
My question is, is it save to use multiple addChildEventListener into one Query, or is there any performance issues with that? All I want to accomplice is reusing the fiDbQuery instance without the need to add addChildEventListener every single time I modify the fiDbQuery.
Thank You for your help.
EDIT:
all those query I make use the exact same listener. Only one listener for all those query which populate a listView.
You don't need to add the addChildEventListener again. What you need to do, is to change the logic of your actions a little bit.
First define all your queries like this:
nameQuery = fiDbRef.child("users").orderByChild("name");
ageQuery = fiDbRef.child("users").orderByChild("age");
//and so on
To solve your problem, you only need to use an if else-if statement which sounds like this:
Query query;
if(nameButton.isClicked()) {
query = nameQuery;
} else if(ageButton.isClicked()) {
query = ageQuery;
}
query.addChildEventListener( // listener. //);
This means that according on which button the click was made, you are using the desired query. With this code, you just use the addChildEventListener once.
Hope it helps.
I'm using greenDAO in my android app to display a list of objects in a RecyclerView. I have a subclass of RecyclerView.Adapter that takes a list of objects which are greenDAO entities.
What I do in onCreate is:
Create an instance of my adapater passing null for my list. This is just to make the adapter known to the RecyclerView below.
Initialize the RecyclerView with layout and adapter.
Call a method that asynchronously queries the data using greenDAO and upon success updates the adapter with the actual list of objects so they are displayed.
This is the relevant code:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mListAdapter = new MyRecyclerAdapter(null);
mList.setHasFixedSize(true);
mList.setLayoutManager(mLayoutManager);
mList.setAdapter(mListAdapter);
refreshItems();
}
public void refreshItems()
{
AsyncSession asyncSession = ((App)getApplication()).getDaoSession().startAsyncSession();
asyncSession.setListenerMainThread(new AsyncOperationListener()
{
#Override
public void onAsyncOperationCompleted(final AsyncOperation operation)
{
if (operation.isCompletedSucessfully())
mListAdapter.setItems((List<Item>) operation.getResult());
}
});
asyncSession.loadAll(Item.class);
}
This works pretty well. Now I noticed, that of course the method that queries the database via greenDAO is invoked every time I rotate the activity or come back to it from another activity. That's pretty clear, since I'm calling that method from onCreate.
My question is: is it best practice to do this like I'm doing it (requery DAO every time) or should I make my objects parcelable and save the list I have in onSaveInstanceState and restore it in onRestore instead of requerying DAO?
What you're doing is completely valid and you don't need to save the queried data in onSaveInstanceState(), use in-memory cache, or any other optimization (even if GreenDAO wouldn't have internal cache).
In fact, you're more than all-right because you perform the query asynchronously - GreenDAO's creators kind of claim that the queries can be executed on UI thread in most cases (which I find hard to agree with).
I would also suggest that you perform data query in onStart() instead of onCreate(). I personally think that onCreate() should be used only for operations you would otherwise perform in the constructor (e.g. fields initializations). Another reason to perform this query in onStart() is that if the user leaves your application for a long time and then gets back to it, the data might get outdated (e.g. due to background syncs by SyncAdapter) and you'll want to refresh it.
The last piece that you might want to add is "data change notifications". You will want this mechanism to be in place if the data that you query and display to the user can change without user's interaction (e.g. due to background syncs by SyncAdapter). The concept is simple - Activity registers for notifications about data change in onCreate(), and if notification received you perform re-query in order to make sure that the user sees an up-to-date data.
I can't claim that the above are "best practices", but they are good practices that work well.
Lazy list:
As #pskink suggested in his comment, you could also employ LazyList. Be aware, though, that it doesn't obviate a need for async query of data. Usage of LazyList allows you to perform the query as usual, but load the results into memory in on-demand way. This might be useful if you expect the query to produce lots of data.
In my opinion, however, one should optimize the code only if actual performance problem is being observed. So, unless you know ahead of time that a particular query produces thousands of results, I say you don't need LazyList.
I have a node in Firebase getting continually updated with information from a logfile. The node is lines/ and each child of lines/ is from a post() so it has a unique ID.
When a client first loads, I want to be able to grab the last X number of entries. I expect I'll do this with once(). From then on, however, I want to use an on() with child_added so that I get all new data. However, child_added gets all data stored in the Firebase and, after the initial setup, only want the new stuff.
I see that I can add a limitToLast() on the on(), but, if I say limitToLast(1) and a flood of entries come in, will my app still get all the new entries? Is there some other way to do this?
You need to include a timestamp property and run a query.
// Get the current timestamp
var now = new Date().getTime();
// Create a query that orders by the timestamp
var query = ref.orderByChild('timestamp').startAt(now);
// Listen for the new children added from that point in time
query.on('child_added', function (snap) {
console.log(snap.val()
});
// When you add this new item it will fire off the query above
ref.push({
title: "hello",
timestamp: Firebase.ServerValue.TIMESTAMP
});
The Firebase SDK has methods for ordering, orderByChild() and methods for creating a range startAt(). When you combine the two you can limit what comes back from Firebase.
I think there is a problem in #David East's solution. He is using the local timestamp which may cause problem if the time is not accurate in client device. Here is my suggested solution (iOS Swift):
Using observeSingleEvent to get the complete data set
Then returned it in reversed order by reversed()
Get the last timestamp by for example data[0].timestamp
Using queryStarting for timestamp
self._dbref.queryOrdered(byChild: "timestamp").queryStarting(atValue: timestamp+1)
.observe(.childAdded, with: {
snapshot in
print(snapshot.value)
})
You have the right idea. child_added should be called only for the new nodes. Without source code it's hard to tell why you get all the data in your child_added event.
You can check the chat demo app to see how they load new chat messages. The use case sounds similar.
https://github.com/firebase/firechat/blob/master/src/js/firechat.js#L347
Here's temporary but quick solution:
// define a boolean
var bool = false;
// fetch the last child nodes from firebase database
ref.limitToLast(1).on("child_added", function(snap) {
if (bool) {
// all the existing child nodes are restricted to enter this area
doSomething(snap.val())
} else {
// set the boolean true to doSomething with newly added child nodes
bool = true;
}
});
Disadvantage: It will load all the child nodes.
Advantage: It will not process existing child nodes but just the newly added child nodes.
limitToLast(1) will do the work.
I am attaching a picture of my firebase database. I am at OPL and I want to go to its parent. Is there somekind of "GetParent()" kind of fuction, like "GetChildren" that can take me there?
To read the data from database, you need to set listeners at appropriate locations. These listeners provide values for one time as well as for updates with the methods onChildAdded() and onChildChanged().
For your case, you should attach a childListener on the ActionList by using the line
mDatabase.child('ActionList').addChildEventListener(listenerName);
Set up the onChildAdded() method of this listener which provides a snapshot. Traversing through the children from that snapshot using dataSnapshot.getChildren() and to get their values use this function child.getKey() method.
My current Android application uses Firebase to store user data.
My data has the following "structure" (and/or levels)
my-android-app.firebaseio.com/level0/level1/UUID1/UUID2
data objects are stored at UUID2 that resemble this
{
"Students":[
{
"Name":"Amit Goenka",
"Major":"Physics"
},
{
"Name":"Smita Pallod",
"Major":"Chemistry"
},
{
"Name":"Rajeev Sen",
"Major":"Mathematics"
}
]
}
I need to be able to detect when child data is added, removed or changed etc
below (at) UUID2, however I only want to set up my ChildEventListener using this path
android-app.firebaseio.com/level0/level1
is this possible?
During my test so far I have had to specify the full path of:-
my-android-app.firebaseio.com/level0/level1/UUID1/UUID2
Firebase will fire a child_* event for any change under the level where you register the correct listener. But it will only fire child_added/child_removed for nodes added/removed directly under the level where you registered. For other changes, it will fire child_changed and possibly child_moved.
Your options:
flatten the tree
listen to value events
listen to all child_ events and figure out the changes