This question already has an answer here:
ArrayList not updating inside onChildAdded function
(1 answer)
Closed 4 years ago.
I want to get the whole value(0~5) from destinations
This is part of json data in realtime database
{"results" : [{
"destin_num" : 6,
"destinations" : [ "asdf", "qwwee", "zzzxcv11", "jkkk", "lkjhhg", "zxcvb" ],
"name" : "asdfasdfff",
"order" : 2,
"required_time" : "6hours" }]}
the value I want to put in the String List , I use ChildEventListener() to add the value into List, but the value can't put into the List after finish the ChildEventListener().There will be nothing in List direction How to get the values?
List<String> direction=new ArrayList<String>();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_maps);
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
DatabaseReference reference_contacts = FirebaseDatabase.getInstance().getReference("results").child("1").child("destinations");
reference_contacts.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String data=dataSnapshot.getValue().toString();
direction.add(data);
}
#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) {
}
});
for(int i=0;i<direction.size();i++){
Log.e("Get data",direction.get(i));
}
}
It's because data is loaded from Firebase asynchronously. Your for loop printing the values is actually run before onChildAdded ever gets called. It's easiest to see this by adding some logging:
Log.i("Firebase", "Before adding listener");
reference_contacts.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.i("Firebase", "In onChildAdded");
}
#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) {
throw databaseError.toException(); // don't ignore errors
}
});
Log.i("Firebase", "After adding listener");
When you run this code it prints:
Before adding listener
After adding listener
In onChildAdded
That is probably not the order you expected. But is completely explains why the direction list is empty when you print it, the data hasn't been loaded yet, so onChildAdded has never been invoked.
The solution is typically to add the code that requires the data into the onChildAdded method:
So:
reference_contacts.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String data=dataSnapshot.getValue().toString();
direction.add(data);
Log.i("Got data",direction);
}
#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) {
throw databaseError.toException(); // don't ignore errors
}
});
If you only want to show the data once everything is loaded, you can use a ValueEventListener instead:
reference_contacts.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot dataSnapshot: snapshot.getChildren()) {
String data=dataSnapshot.getValue(String.class);
direction.add(data);
}
for(int i=0;i<direction.size();i++){
Log.e("Get data",direction.get(i));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
Note that this is an incredibly common source of confusion for developers new to Firebase (and asynchronous APIs in general). I highly recommend studying some of the answers to some of these previous questions about the topic:
Setting Singleton property value in Firebase Listener
getContactsFromFirebase() method return an empty list (which also shows how to define a custom interface to allow the code that requires the data to be outside onChildAdded/onDataChange.
can't get values out of ondatachange method
ArrayList not updating inside onChildAdded function
Related
My problem is the same with title,firebase onchildadded() returns same data 3 times in listview.I tried many things but still couldnt found solution.
My code is there;
public void ReceiveMessages(){
childEventListener = message_dbref.child(user_id).child(user2_id).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Message message = dataSnapshot.getValue(Message.class);
message_dbref.removeEventListener(childEventListener);
user_dbref.removeEventListener(valueEventListener);
messageArrayList.add(message);
messageAdapter.notifyDataSetChanged();
}
#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) {
}
});
}
So you should not be using a childEventListener because it fires whenever any child of the parent node changes.
You can check if you're updating other children of the parent node on which your childEventListener is set, along with others. That must be the case, for it to fire 3 times.
So you should rather focus on using a singleValueEventListener or a valueEventListener on the same reference you're using right now.
Read more about childEventListeners here.
Understand more about reading and writing on Firebase here.
I am facing a problem with adding older data to RecyclerView with SwipeRefreshLayout so, here is how i retrieve from Firebase and put data into RecyclerView
private void messageList() {
referenceMessages.orderByKey().limitToLast(10).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
MessagesInfo messages = dataSnapshot.getValue(MessagesInfo.class);
result.add(messages);
adapter.notifyDataSetChanged();
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
MessagesInfo messages = dataSnapshot.getValue(MessagesInfo.class);
result.remove(messages);
adapter.notifyDataSetChanged();
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
as you can i see, I set limit of retrieved messages to last 10, what means the 10 newest messages will be displayed. So far everything works great, the messages are displayed like that
-20thmessage(the oldest)-
-21stmessage-
-...-
-29thmessage-
-30thmessage(the newest)-
now i use SwipeRefreshLayout like that
private void swipe(){
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
referenceMessages.orderByKey().endAt("-LHUhkub4iaZNNzmtUcs").limitToLast(10).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
MessagesInfo messages = dataSnapshot.getValue(MessagesInfo.class);
recyclerView.scrollToPosition(0);
result.add(0, messages);
adapter.notifyDataSetChanged();
}
#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) {
}
});
}
});
}
and it retrieves data properly, i mean previous 10 messages before hardcoded endAt() but after swiping places data not properly. If i dont use index 0
new data is placed at the botoom of latest message and it looks like that
-30thmessage(the newest)-
-10thmessage-
-...-
-19thmessage-
-20thmessage(the newest)-
and when i use index 0 data is added from the top of already loaded messages but like that
-20thmessage(the oldest)-
-19thmessage-
-...-
-11ththmessage-
-10th-
so it is added upside-down. Hope you understand me :) any inputs are highly appreciated
You might try clearing the list first and then add the new messages.
Android SwipeRefreshLayoutBasic
public void readUniversity(){
ChildEventListener uniListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
//Log.d(LOG_TAG, dataSnapshot.getKey());
University uni = dataSnapshot.getValue(University.class);
universitieslist.add(uni); //Adding to an ArrayList of type University
//Log.d(LOG_TAG, universitieslist.size()+"is the size");
arrayofUniversities.add(uni.getName()); //Adding name of University to list
//Log.d(LOG_TAG, adapter.getCount() + " items");
//Log.d(LOG_TAG, arrayofUniversities.size()+" is the size");
adapter.notifyDataSetChanged();
}
#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) {
}
};
DatabaseReference uniReference = database.getReference("note-retrieve/Universities");
uniReference.addChildEventListener(uniListener);
}
The code is pretty much self explanatory. I am using arrayofUniversities a list to populate a spinner so I need an adapter. I am populating the spinner with the names of the Universities
I am storing the University Objects in universitieslist
The problem I have is when I leave the readUniversity() method I lose every element in universitieslist and arrayofUniversities even though my spinner is populated with these.
Its very strange as the list should have the objects stored.
Also, I have my adapter at the very top of the code in onCreate().
Try this way. Hope it will work .. :)
private Arrylist<University > universityList = new ArryList<>();
private Arrylist<String> universitysName = new ArryList<>();
mDatabase.getReference().child("path")
.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if (dataSnapshot != null) {
University mUniversity = dataSnapshot.getValue(University
.class);
universityList.add(mUniversity);
universitysName.add(mUniversity.getName);
}
}
#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) {
}
});
The problem seems to be with the adapter, if you can see the logs but the adapter is not updating, then is because the adapter is not being notified the data set has changed
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if (dataSnapshot != null) {
University mUniversity = dataSnapshot.getValue(University
.class);
universityList.add(mUniversity);
universitysName.add(mUniversity.getName);
//UPDATE YOUR ADAPTER HERE
//spinnerAdapter.notifyDataSetChanged();
}
}
But I think the real problem is the origin of your current problem: You are getting each child. This is a way to do it, but for this case where you need all the data first and when that is ready show the spinner with the elements to the user, you should better get all the list elements in one query.
database.getReference("note-retrieve/Universities")..addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
University mUniversity = dataSnapshot.getValue(University
.class);
universityList.add(mUniversity);
universitysName.add(mUniversity.getName);
//ONCE THIS IS DONE, THEN PASS THE ARRAY TO THE ADAPTER
}
//The loop here is over, so you can set the adapter with the full list of data/universities
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I am trying to set my Firebase reference before my child listener kicks in and loads my data. I know onCreate is ahead of onResume but my code below seems to contradict it as my reference is still what I have set in my onResume(). It seems like what I set up in onCreate doesn't go before what I have in onCreate().
Why is that?
I am trying to read from a template in my database if another node doesn't have the user registered as a child yet.
Any hint would be appreciated.
p.s. I have logged out the references and the Log in OnResume() goes first as well.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.services_activity);
app = FirebaseApp.getInstance();
database = FirebaseDatabase.getInstance(app);
auth = FirebaseAuth.getInstance(app);
storage = FirebaseStorage.getInstance(app);
username = auth.getCurrentUser().getUid();
databaseRef = database.getReference("serv_hst");
servTempltRef = database.getReference("serv_tmplt");
databaseRef.child(username).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
//I am trying to set my reference that I will use in OnResume() ..!!
if (dataSnapshot.hasChildren()) {
servTempltRef = database.getReference("serv_hst");
}
}
#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) {
}
});
}
#Override
protected void onResume() {
super.onResume();
servTempltRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
}
#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) {
}
});
The code inside the onChildAdded() will be executed asynchronously only when the data will be downloaded from Firebase Database.
So, after that the execution flow will execute the line in which there is the .addChildEventListener() method, it will go ahead and will continue to execute the next lines...
In your case, the onCreate() has no other lines to be executed. So, due to the Activity lifecicle, the onResume() method will be executed without the data you need.
My data on Firebase like this:
"node": {
"node1": {
"value1": value,
"value2": value,
"subSubNode":{
//....values....//
},
},
"node2": { ... },
"node3": { ... }
//...and so on ..//
}
First: I want to get all data from Firebase and render to my tree so I used addListenerForSingleValueEvent() function.
Then: I used addChildEventListener() function to listen when a node or child change, remove or added...
private void getData(Query ref){
ref.addListenerForSingleValueEvent(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
**I render data on my tree here**
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
ref.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.i(TAG, "onChildAdded");
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Log.i(TAG, "onChildChanged");
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.i(TAG, "onChildRemoved");
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
Log.i(TAG, "onChildMoved");
}
#Override
public void onCancelled(FirebaseError firebaseError) {
Log.i(TAG, "onCancelled");
}
});
}
But onChildAdded() method in addChildEventListener() function is always run first. Any idea or solution for me? Do I use addChildEventListener() function on that position correct?
The methods in ChildEventListener fire for both initial data and for subsequent changes to that data.
Once you call addChildEventListener(), you will get a call to onChildAdded() for each existing child node. You can use these event to build the initial tree. After the initial data has been added, you will receive a calls to all onChild... methods as the data changes.
So don't register for addListenerForSingleValueEvent() and simply build the tree from the methods of ChildEventListener.
Alternatively if you want to separately handle the initial data, you can make use of one of the event guarantees that Firebase has:
Value events are always triggered last and are guaranteed to contain updates from any other events which occurred before that snapshot was taken.
boolean isInitialValueLoaded = false;
ref.addListenerForSingleValueEvent(new ValueEventListener(){
public void onDataChange(DataSnapshot dataSnapshot) {
**I render data on my tree here**
isInitialValueLoaded = true;
}
public void onCancelled(FirebaseError firebaseError) {
}
});
ref.addChildEventListener(new ChildEventListener() {
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if (!isInitialValueLoaded) return;
Log.i(TAG, "onChildAdded");
}
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
if (!isInitialValueLoaded) return;
Log.i(TAG, "onChildChanged");
}
public void onChildRemoved(DataSnapshot dataSnapshot) {
if (!isInitialValueLoaded) return;
Log.i(TAG, "onChildRemoved");
}
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
if (!isInitialValueLoaded) return;
Log.i(TAG, "onChildMoved");
}
public void onCancelled(FirebaseError firebaseError) {
Log.i(TAG, "onCancelled");
}
});