After create data on Firebase. I try retrieving data from Firebase. But I have problem, I think may be Log.d(TAG,list.size()) run before ref.addChildEventListener(childEventListener); complete. Who can help me ?
public class NewFirebase extends AppCompatActivity {
List < Product > list = new ArrayList < > ();
private static final String TAG = "Firebase";
DatabaseReference ref;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Firebase.setAndroidContext(this);
ref = FirebaseDatabase.getInstance().getReference();
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
// A new comment has been added, add it to the displayed list
Product comment = dataSnapshot.getValue(Product.class);
for (DataSnapshot child: dataSnapshot.getChildren()) {
Product post = child.getValue(Product.class);
list.add(post);
}
// ...
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "postComments:onCancelled", databaseError.toException());
}
};
ref.addChildEventListener(childEventListener);
Log.d(TAG, list.size() + "");
}
class RetrievingData extends AsyncTask < Void, Void, Void > {
#Override
protected Void doInBackground(Void...voids) {
return null;
}
}
}
You need to take a second approach to how you are structuring your code, or even take a look at the definition of callback/listener itself.
The addChildEventListener() method assigns a callback and initiates a query for retrieving the result. That is, of course, done in background.
Using listeners will never work that way, that's why they were made for, to don't follow line-by-line execution. If you want to get some result from them, you need to put the code inside their methods, which is when they give you some response. Can take milliseconds, seconds, even minutes, but don't expect to be so immediate to be quicker than the execution of the next line that it was posted to execution.
Take a look at https://www.firebase.com/docs/android/guide/retrieving-data.html.
If you want to see the size of the list that you get from Firebase database, you should use addValueEventListener instead of addChildEventListener
List<Product> commentList = new ArrayList<>();
myRef.addValueEventListener(new ValueEventListener() {
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot postSnapshot: snapshot.getChildren()) {
Product comment = postSnapshot.getValue(Product.class);
commentList.add(comment);
}
// here you can print the size of your list
Log.d(TAG,list.size())
}
public void onCancelled(FirebaseError firebaseError) {
System.out.println("The read failed: " + firebaseError.getMessage());
}
});
Related
I want to get particular value using from DataSnapshot.
I am attaching the screenshot here so kindly check and help me to get particular value from Realtime Database.
Actually I am implementing chat application in which I want to get value of user from group_list.
Here is my code.
private void loadTotalGroupList() {
referenceMainUrl = FirebaseDatabase.getInstance().getReferenceFromUrl("https://pure-coda-174710.firebaseio.com");
referenceGroupList = referenceMainUrl.child("group_list");
//Check if child is available or not.
referenceGroupList.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
Log.e("dataSnapshot"," ==>"+dataSnapshot);
Map<String, Object> newPost = (Map<String, Object>) dataSnapshot.getValue();
Log.e("newPost"," ==>"+newPost);
Log.e("user: ","==>" + newPost.get("user")); // Here I am getting null value
} else {
Log.e("Child not found", " >>>");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
and log showing like this. DataSnapshot { key = group_list, value = {First Group={-KtBH9gnTszNxcXjNu9A={message=assaasas, user=sakib}}} }
I have resolved my issue by using addChildEventListener
referenceGroupList.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String prevChildKey) {
Log.e("dataSnapshot KEY", " ==>" + dataSnapshot.getKey());
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String prevChildKey) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String prevChildKey) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Here dataSnapshot.getKey() returns all sub child of it.
You're listening to the location /group_list in your code. That means a a snapshot from that location will contain the following:
/First Group
/-KtBH9gnTszNxcXjNu9A
message = "..."
user = "..."
If you want to get the user value from that location, you'll have to dig into it using each intermediate path:
dataSnapshot.child("First Group").child("-KtBH9gnTszNxcXjNu9A").child("user").getValue()
Or more simply:
dataSnapshot.child("First Group"/-KtBH9gnTszNxcXjNu9A/user").getValue()
You can't skip the middle paths in the snapshot. Alternatively, you may want to listen to a location closer to the value you want:
referenceMainUrl.child("group_list/First Group/-KtBH9gnTszNxcXjNu9A");
This code runs in a loop and gives you values for all the 'user's
DatabaseReference dRef = FirebaseDatabase.getInstance().getReference().child("group_list").child("First_Group");
dRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot snapshot : dataSnapshot.getChildren()){
String userName = snapshot.child("user").getValue(String.class);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
How do I search for users based on their usernames? I have looked at numerous SO posts on this matter but am still unable to achieve what I want to do.. I have tried to apply what I saw in those posts and is shown below:
DatabaseReference usersRef = FirebaseDatabase.getInstance().getReference("users");
usersRef.orderByChild("username")
.startAt(queryText)
.endAt(queryText+"\uf8ff");
usersRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
searchList = new ArrayList<>();
for (DataSnapshot postSnapshot: snapshot.getChildren()) {
User user = postSnapshot.getValue(User.class);
Log.d("USER: ", "" + user.getUsername());
searchList.add(user);
}
adapter = new UserCardAdapter(getContext(), searchList);
recyclerView.setAdapter(adapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e("onQueryTextChange: " ,databaseError.getMessage());
}
});
However, all users are still retrieved. I have seen the usage of startAt() and endAt() supposedly work for others on other posts but I cannot manage to get it to work for me..
This is how the user data is stored:
User Data Structure
You almost done right but you should add addListenerForSingleValueEvent after the database reference that already apply orderBy() , startAt(), endAt() like this.
usersRef.orderByChild("username")
.startAt(queryText)
.endAt(queryText+"\uf8ff")
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
searchList = new ArrayList<>();
for (DataSnapshot postSnapshot: snapshot.getChildren()) {
User user = postSnapshot.getValue(User.class);
Log.d("USER: ", "" + user.getUsername());
searchList.add(user);
}
adapter = new UserCardAdapter(getContext(), searchList);
recyclerView.setAdapter(adapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e("onQueryTextChange: " ,databaseError.getMessage());
}
});
Your use of orderBy(), startAt(), and endAt() is correct according to the documentation.
But the addListener method must be applied directly to the object returned by the chain of orderByChild(), startAt(), and endAt() methods, and not in a new statement on the DatabaseReference retrieved with ... .getReference("users").
If you use a ChildEventListener:
DatabaseReference usersRef = FirebaseDatabase.getInstance().getReference("users");
usersRef.orderByChild("username")
.startAt(queryText)
.endAt(queryText+"\uf8ff");
.addChildEventListener(new ChildEventListener() {
List<User> searchList = new ArrayList<>();
#Override public void onChildAdded(DataSnapshot dataSnapshot, String s) {
User user = dataSnapshot.getValue(User.class);
Log.d("USER: ", "" + user.getUsername());
searchList.add(user);
}
#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) {}
});
I'm starting to use firebase and everything looks great but I have 50k records to show inside a recyclerview and I can't figure out how to paginate or something similar because load everything at once takes too much time.
I used childEventListener and FirebaseUI as well with no luck.
UPDATE 1:
Finally I got working for the update 1, this is not the best solution for this but I think I'm going to make some changes later.
private String lastKey = null;
private List<Comment> commentList = new ArrayList<>();
private final static int QUERY_LIMIT = 20;
private int page = 0;
private void loadComments() {
page++;
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Comment comment = dataSnapshot.getValue(Comment.class);
commentList.add(comment);
adapter.notifyDataSetChanged();
lastKey = String.valueOf(comment.getTimestamp());
}
#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) {
}
};
if (lastKey != null) {
commentList.clear();
adapter.notifyDataSetChanged();
comments.child(id_post).orderByChild("timestamp").endAt(lastKey).limitToLast(QUERY_LIMIT * page).addChildEventListener(childEventListener);
} else {
comments.child(id_post).orderByChild("timestamp").limitToLast(QUERY_LIMIT).addChildEventListener(childEventListener);
}
}
You can add a value event listener to limit the number of of results you would like to receive, here is a quick example from firebase-ui
ref.limitToLast(5).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot msgSnapshot: snapshot.getChildren()) {
Chat msg = msgSnapshot.getValue(Chat.class);
Log.i("Chat", chat.getName()+": "+chat.getText());
}
}
Use the method limitToLast(5) to get the last 5 objects that were added. Other methods with regards to this include limitToFirst(), limitToLast(), startAt(), endAt(), and equalTo()
More information can be found Here.
I am using this method to show 8 Million Records stored in the Real-time Database, 50 at a time with pagination.
private void getUsers(String nodeId) {
Query query;
if (nodeId == null)
query = FirebaseDatabase.getInstance().getReference()
.child(Consts.FIREBASE_DATABASE_LOCATION_USERS)
.orderByKey()
.limitToFirst(mPostsPerPage);
else
query = FirebaseDatabase.getInstance().getReference()
.child(Consts.FIREBASE_DATABASE_LOCATION_USERS)
.orderByKey()
.startAt(nodeId)
.limitToFirst(mPostsPerPage);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
UserModel user;
List<UserModel> userModels = new ArrayList<>();
for (DataSnapshot userSnapshot : dataSnapshot.getChildren()) {
userModels.add(userSnapshot.getValue(UserModel.class));
}
mAdapter.addAll(userModels);
mIsLoading = false;
}
#Override
public void onCancelled(DatabaseError databaseError) {
mIsLoading = false;
}
});
}
You also don't have to store the last key separately.
If you are not able to understand it fully then you can read the blog post here which explains everything.
I am trying to retrieve a list of children at a specific location, but I get only gibberish when I do what I think is right.
My code for retrieving data:
final String userId = FirebaseAuth.getInstance().getCurrentUser().getUid();
mDatabase = FirebaseDatabase.getInstance().getReference();
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
adapter.add(dataSnapshot.child(userId).getChildren().toString());
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());
adapter.add(dataSnapshot.child(userId).getChildren().toString());
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());
adapter.add(dataSnapshot.child(userId).getChildren().toString());
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "postComments:onCancelled", databaseError.toException());
Toast.makeText(getActivity(), "Failed to load entries.",
Toast.LENGTH_SHORT).show();
}
};
mDatabase.addChildEventListener(childEventListener);
Screenshot of datastructure
What I am trying to retrieve a list of the children below the userId.
What am I doing wrong?
I don't think you should add the ChildEventListener to the root node of your firebase database since this would download all the data in your database.
Instead, call it on the specific user since you only want it for that UID.
final String userId = FirebaseAuth.getInstance().getCurrentUser().getUid();
mDatabase = FirebaseDatabase.getInstance().getReference(userId);
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
// maybe use a POJO here to collect data easily
// the toString() method is maybe for testing??
adapter.add(dataSnapshot.getValue().toString());
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
// This part should have appropriate code
// Unless of course you actually want to add it
//to the adapter everytime
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
// This part should have appropriate code
// Unless of course you actually want to add it
//to the adapter everytime
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "postComments:onCancelled", databaseError.toException());
Toast.makeText(getActivity(), "Failed to load entries.",
Toast.LENGTH_SHORT).show();
}
};
mDatabase.addChildEventListener(childEventListener);
I'm using OnChildAdded event for get all data from database on Android.
The first time and many time later, It works well.
But something, onChildAdded don't be call for old child anymore.
Uninstall and install app again make it work again.
I checked permission and added event.
I don't know what's wrong and what have to do to fix it.
This code, I use for init
prayersRef = database.child("prayers");
Log.w(TAG, "FireBaseDatabaseManager");
prayersListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
Prayer prayer = dataSnapshot.getValue(Prayer.class);
prayer.uid = dataSnapshot.getKey();
Log.w(TAG, "onChildAdded:" + prayer.uid);
if (delegate != null) {
delegate.OnPrayerAdded(prayer);
}
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
Log.w(TAG, "onChildChanged:" + dataSnapshot.getKey());
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.w(TAG, "onChildRemoved:" + dataSnapshot.getKey());
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
Log.w(TAG, "onChildMoved:" + dataSnapshot.getKey());
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "onCancelled", databaseError.toException());
}
};
Then I add event in here
Log.i(TAG, "Add Child Event Listener");
prayersRef.addChildEventListener(prayersListener);
OnChildAdded event is used to refreshing data. Try to retrieve data using ListenerForSingleValueEvent :) then in onDataChange you can put all downloaded items in list :) like:
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
List<Model> models = new ArrayList<>();
for(DataSnapshot snapshot : dataSnapshot.getChildren()) {
Model model = snapshot.getValue(Model.class);
models.add(model);
}
}