is there any ways to read data from Firebase once the Activity is loaded. At this moment I am using the regular valueEventListener, but in order for it to work, there has to be some sort of a change in the database
mDatabaseReference.child("Users").child(mUser.getUid()).
child("Posts").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
arrayOfQuestionForms.clear();
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
QuestionForm tempQuestionForm = postSnapshot.getValue(QuestionForm.class);
arrayOfQuestionForms.add(tempQuestionForm);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
What I am looking for is some way to read data from Firebase without using listeners. I have looked at other similar posts but I don't think there is a clear answer for this yet.
There is no way for reading data from a Firebase database without using listeners. Everything is about listeners when it comes to Firebase. It's true that when setting a value, we just need to use a method named setValue() directly on the reference. Unfortunately, there is no method within Firebase, let' say getValue(), which acts in the same way as setValue().
To solve this, i recommend you using addListenerForSingleValueEvent.
Add a listener for a single change in the data at this location. This listener will be triggered once with the value of the data at the location.
in order for it to work, there has to be some sort of a change in the database
This is not true and a common source of confusion for developers.
With your current code, Firebase will immediately start reading the data from the server. Once it gets that data, it invokes your onDataChange().
From the documentation:
This method is triggered once when the listener is attached and again every time the data, including children, changes.
for such purpose I used different kind of listener - ChildEventListener. It has different #Override methods. The method onChildAdded returns every child-nodes of the node when called first time (i.e. on activity start).
Put attention - maybe you will need to slightly change the reference to DB (trim back one hierarchy level), to point to the parent node. If you expanded snapshot of your DB structure, I can look.
Here is updated code (sorry is made any typo - I couldn't test it as have no your DB :)
mDatabaseReference.child("Users").child(mUser.getUid()).child("Posts").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
arrayOfQuestionForms.clear();
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
QuestionForm tempQuestionForm = postSnapshot.getValue(QuestionForm.class);
arrayOfQuestionForms.add(tempQuestionForm);
}
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Related
I know that when we retrieve data from Firebase ,it will be asynchronous, so ussally i will put all the code inside addChildEventListener, like example i want to sort userList below. But i am confused, if the List is really big, like million Users, so it means the method sortUser(user) will be called million times ? Can anyone explain this to me, I'm new to firebase
myRef.child("User").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
User user= dataSnapshot.getValue(User.class);
userList.add(user);
sortUser(userList);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
You currently use a ChildEventListener, which means your onChildAdded gets called for each child node immediately and then later whenever a new child is added. This indeed can be a lot of invocations.
If you use a ValueEventListener, its onDataChange will only be called once for the initial data (no matter how many child nodes there are), and then once for each change.
By adding a ValueEventListener to your current set up, you can keep things simple: add the child nodes to the lit like you're already doing, but only sort in onDataChange.
myRef.child("User").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
sortUser(userList);
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
};
Firebase will only synchronize the data for the User node once, even when you have two listeners on it.
You should probably retrieve the data sorted server side by using order-by methods and then listen to that one.
var userRef = firebase.database().ref('posts').orderByChild('Users');
If I guess correctly, you would not need separate sorting client side.
You can also filter data. Do refer the docs
I am new to Firebase Real-time Database and don't have an idea that how to fetch only the recent updated object from the table.
I have tried using "ChildEventListener" but when I initialize the Listener for the first time it fetches the last row from the database.
It should not be fetched if it was not updated.
I want object only when it is updated or newly added.
I have done this,
databaseReference.child("last_chat").orderByChild("s_id").equalTo(preference.getUserData().getId()).limitToLast(1).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.e("dataSnapShot", dataSnapshot.toString() + " " + s);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Log.e("dataSnapShotChagned", dataSnapshot.toString() + " " + s);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.e("dataSnapShotRemoved", dataSnapshot.toString());
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e("onCalcnelle", databaseError.toString());
}
});
Thank you in advance.
I have solved the issue.
I just need to put my code inside the OnChildChanged instead of putting my code in OnChildAdded.
Log dataSnapShot shown when first running the application, whereas as soon as I change or update object dataSnapShotChagned is shown.
Thus it worked for me, OnChildAdded method is called every time while initializing the listener where OnChildChanged method is called only when there is a change in a particular object.
Thanks a lot all for the help.
public ValueEventListener addValueEventListener (ValueEventListener listener)
Also: Google Play services
Add a listener for changes in the data at this location. Each time time the data changes, your listener will be called with an immutable snapshot of the data.
Firebase Doc
Hope this helps.
discussionReference.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Discussion discussion = dataSnapshot.getValue(Discussion.class);
discussionAdapter.add(discussion);
//cancel spinner
t[0]++;
if(t[0]>=2)setNormalScreen();
ObjectsKeysManager.discussionIds.add(dataSnapshot.getKey());/*this is my static variable*/
discussionAdapter.notifyItemInserted(ObjectsKeysManager.discussionIds.size()-1);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Discussion newDiscussion = dataSnapshot.getValue(Discussion.class);
String discussionKey = dataSnapshot.getKey();
int discussionIndex = ObjectsKeysManager.discussionIds.indexOf(discussionKey);
if(discussionIndex>-1)
{
discussionAdapter.update(newDiscussion, discussionIndex);
discussionAdapter.notifyItemChanged(discussionIndex);
}
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot)
{
Discussion removedDiscussion = dataSnapshot.getValue(Discussion.class);
int index = ObjectsKeysManager.discussionIds.indexOf(dataSnapshot.getKey());
ObjectsKeysManager.discussionIds.remove(index);/*here its changed*/
discussionAdapter.removeElement(removedDiscussion);
discussionAdapter.notifyDataSetChanged();
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
what if the user1 adds child, user2 removes child, user3 updates another child at the exact same time. Its obvious that ObjectsKeysManager.discussionIds is in all . How to lock the variable ObjectsKeysManager.discussionIds ? or how to professionaly implement this ?
additionally
do onChildAdded(), onChildChanged(), onChildRemoved() wait for each other?
To solve this problem, i recomand you using transaction operation. This is the best way in which you can achieve this, when we are talking about concurrent modifications.
I recomand you also using ValueEventListener that has only one method named onDataChange() which reads and listens for changes to the entire contents of a specific path.
Hope it helps.
as #Alex Mamo suggested I have been studying Transaction class and transaction only helped for a single node, but in my case the big problem was to make onChildEventListener() thread safe (I dont know if it is by default but didn't take the risk) ,the solution I came up is making a method public synchronized void onChildModified(DataSnaphot dataSnaphot, String type) and redirected onChildChanged() and onChildRemoved() to it by passing the snapshot as parameter, this made any event that happened go directly to the synchronized method and saved the day, about the onChildAdded() its not necessary to make is thread safe reason being in order to change and remove the child it must be added so no need to put it inside onChildModified() (even if you did probably gonna have some errors because of the iteration of the snapShot).
I have the following database structure in Firebase:
In my app when I launch an activity. I want to get all the level0Node's nodes only once when the activity is started. This I achieve like this:
rootNode.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Iterable<DataSnapshot> level0Nodes = dataSnapshot.getChildren();
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
But the problem I am facing is that if any updates occur on the level1Node's, onDataChanged is invoked again which I do not want. How can I achieve this?
You're looking for a SingleValueListener.
Replace your ValueEventListener with one, the code would look like this:
rootNode.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Iterable<DataSnapshot> level0Nodes = dataSnapshot.getChildren();
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
For more info refer to the Official Docs
you have two methods for getting total data in one go from a particular reference.
rootNode.addValueEventListener(new ValueEventListener(){}
// this you are using and according to documentation this is triggered when ever the data is change at this node- rootNode. if you want to use it only once your can remove your listner by calling
rootNode.removeEventListener() method for removing further updates.
use rootNode.addListenerForSingleValueEvent(new ValueEventListener() {}
according to documentation this is only triggered once so when ever there is a change in your child this listner will not trigger and your problem will be solve.
plus i just want to suggest you to read full documentation on firebase as your question was very basic.
I just started using firebase and I'm amazed of how simple are complex things, and how complex simple things can be.
I need to check if a user exists in my Firebase database, and if exists, I need to get its key and value.
I have the key and I try to find it in the DB by going to my user subtree and loking for a child with the same key (ID)
DatabaseReference root_firebase = FirebaseDatabase.getInstance().getReference();
DatabaseReference users_firebase = root_firebase.child("Users");
DatabaseReference friend_found = users_firebase.child(key_searched_friend);
once I have it I try to call some method like
friend_found.getValue();
or
friend_found.child("user_name").getValue();
But it does not exist,
this seems weird since I can do both
friend_found.getKey();
friend_found.child("user_name").getKey();
I can't do this with the overriden method onDataChanged() since the data does not change when I query for this value.
This is my firebase database structure:
{
"RoomNames" : {
"Room-KM5cof0jcoMN8a4g6vC" : {
"room_name" : "TESTrOOM"
},
"Room-KM5dg_WPRdEOT4_oJ1r" : {
"room_name" : "testRoom2"
}
},
"Users" : {
"User-KM5ZaGq0xvjQis05CPF" : {
"user_name" : "Enrique"
}
}
}
You say: I can't do this with the overriden method onDataChanged() since the data does not change when I query for this value
The guide for how to Retrieve Data explains that:
Firebase data is retrieved by attaching an asynchronous listener to a
FirebaseDatabase reference. The listener is triggered once for the
initial state of the data and again anytime the data changes.
So when you attach a listener to a location, onDataChanged() fires and gives you the current value.
In the section titled Read Data Once, the guide states:
In some cases you may want a callback to be called once and then
immediately removed, such as when initializing a UI element that you
don't expect to change. You can use the
addListenerForSingleValueEvent() method to simplify this scenario: it
triggers once and then does not trigger again.
Use as below :
friend_found.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String value = dataSnapshot.getValue(String.class);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.e(TAG,"Error while reading data");
}
});
For a database reference object, the same way one can add an event listener, it can also be removed, using removeEventListener.
Instead of creating an anonymous object like this
friend_found.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String value = dataSnapshot.getValue(String.class);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.e(TAG,"Error while reading data");
}
});
you can create a named object of ValueEventListener and remove it from the database reference object using removeEventListener, at the end of the onDataChange method
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String value = dataSnapshot.getValue(String.class);
friend_found.removeEventListener(valueEventListener);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.e(TAG,"Error while reading data");
}
});
friend_found.addValueEventListener(valueEventListener);
The code inside onDataChange method gets executed only once as the ValueEventListener object is removed as soon as the last line of the method gets executed.