I have to load approx 5000 user data from firebase realtime database and show it to user in list so i need to implement loadmore functionality below is the code which load all the data
mDatabase.child(getString(R.string.users)).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Map<String, Object> newPost = (Map<String, Object>) dataSnapshot.getValue();
usersList.clear();
if(newPost!=null){
// perform data manupulation
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
loading.dismiss();
}
});
I have tried limittofirst and limittolast but its not sort out my problem as i need data in-betweeen after first load and also like to optimize dowload as well so that data wont get downloaded again and again i.e first 100 then first 200 and so on its dowloaded the same data again and consume the firebase bandwidth
Can you please tell me how to implement loadmore functionality in firebase realtime database in effective way?
Related
I'm building an app which will show Posts stored on firebase. The list of Post needs to be paginated fetching most recent 10 posts at a time.
Here is the methode to load post:
private void readsposts(){
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("Posts");
reference.keepSynced(true);
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
postList.clear();
for(DataSnapshot snapshot:dataSnapshot.getChildren()){
Post post = snapshot.getValue(Post.class);
for(String id:followingList){
if(post.getPublisher()!=null && post.getPublisher().equals(id)){
postList.add(post);
indication.stopShimmer();
indication.setVisibility(View.GONE);
}
}
if(post.getPublisher()!=null && post.getPublisher().equals(firebaseUser.getUid())){
postList.add(post);
//stop shimmer
indication.setVisibility(View.GONE);
indication.stopShimmer();
}
}
postAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
But above code retrieves entire list of posts instead of limited posts. How can pagination be implemented?
when you fetch data at a location in your database, you also retrieve all of its child nodes.
(because you read "Posts")
so if you Advance record your post key ,otherwise you will download data
so you can new a table of Contents to stockpile your post key
I am fetching JSON array of user ids from a server (not Firebase server).
I also store images of each user in Firebase storage. I have a dataset of Users, that contain user id and user image url. The JSON response is constantly updating, so every call I receive new response from the server with new list of user ids. The only solution I came up with, is to:
Clear dataset > Loop through the JSON Array to add all users to the empty dataset > notify dataset changed.
The problem with this is that it's not efficient: I notify data set changed on each iteration, and also since I clear the dataset every new response (from the remote server), the list refreshes, instead of simply adding / removing the necessary users.
This is how the code looks:
#Override
public void onResponse(JSONArray response) { // the JSON ARRAY response of user ids ["uid1", "uid334", "uid1123"]
myDataset.clear(); // clear dataset to prevent duplicates
for (int i = 0; i < response.length(); i++) {
try {
String userKey = response.get(i).toString(); // the currently iterated user id
final DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference userKeyRef = rootRef.child("users").child(userKey); // reference to currently iterated user
ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
myDataset.add(new User(dataSnapshot.getKey(), dataSnapshot.child("imageUrl").getValue().toString())); //add new user: id and image url
mAdapter.notifyDataSetChanged(); // notify data set changed after adding each user (Not very efficient, huh?)
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage());
}
};
userKeyRef.addListenerForSingleValueEvent(listener);
}
catch (JSONException e) { Log.d(TAG, "message " + e); }
}
You might be interested in DiffUtil.
It uses an efficient algorithm to calculate the difference between your lists. And the cherry on the top is that this can be run on a background thread.
It is an alternative to notifyDataSetChanged() and is sort of an industry standard way for updating your RecyclerView
You can use Firebase Cloud functions.Pass the JSON Array to cloud function and retrieve the updated dataset in one go and notify the recycle view Here is link
In my Application I am using Firebase to retrieve the mobilnumbers of the Users. Therefore I use this code:
databaseUsers.orderByChild("uid").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
users.clear();
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
User contactlists = postSnapshot.getValue(User.class);
users.add(contactlists);
}
ContactList contactAdapter = new ContactList(ContactListActivity.this, users);
listViewContacts.setAdapter(contactAdapter);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Now I have the following question: If the number of users is high, is there a possibility to send only the mobilnumbers of your Phonebook maybe in a list? Otherwise I think the traffic to Firebase might be not so efficient?!
The other opportunity would be to send each number individually but this might be quite complex if the user has many contacts.
At the moment I get all numbers from the server but I need to filter for the right contacts AND I need to display the names of the contact.
What is the best solution to use Firebase as efficient as possible and also get the names of the contacts?
Thank you in advance!
You'll have to:
Loop through the local phone book to find the phone number of each contact.
Execute a query to Firebase for each number.
Add the resulting contact (if any) to the list/adapter and update the view.
So say you've done step 1 and have a list of phone numbers. You'd then loop through those and for each:
for (String phonenumber: phonenumbers) {
Query query = databaseUsers.orderByChild("phonenumber").equalTo(phonenumber);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User contactlists = postSnapshot.getValue(User.class);
users.add(contactlists);
adapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore erors
}
});
}
The call to notifyDataSetChanged() ensures that the adapter know that it needs to update the view.
While the code gets a bit convoluted, it is not as slow as you may initially fear, since Firebase pipelines the requests over a single connection. The performance will mostly depend on the number of users you have in the database, but up to a few hundreds of thousands this should be fine.
My app is using Firebase and there are almost 200 users live at the a given time. Most of the users are complaining that the data doesn't load. I was using ChildEventListener for obtaining the data which keep the connection alive and reflects live changes. There is a limit of 100 connections in the free plan. I guess that is the reason my data is not loading at times. After reading the doc I found another way to read data using ValueEventListener. Below is the code I'm currently using
public void getImages() {
Query imagesQuery = FirebaseDatabase.getInstance().getReference().child("englishDps").child(mChildName).orderByKey().limitToLast(21);
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Image image = dataSnapshot.getValue(Image.class);
image.setNodeKey(dataSnapshot.getKey());
mTempImages.add(image);
if (mTempImages.size() == 21) {
mLastKey = mTempImages.get(0).getNodeKey();
Collections.reverse(mTempImages);
mTempImages.remove(mTempImages.size() - 1);
mImages.addAll(mTempImages);
setAdapter();
}
}
#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 (isAdded()) {
Toast.makeText(getActivity(), "Problem loading more images...", Toast.LENGTH_LONG).show();
}
}
};
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot imageSnapshot : dataSnapshot.getChildren())
{
Image image = imageSnapshot.getValue(Image.class);
image.setNodeKey(imageSnapshot.getKey());
mTempImages.add(image);
if (mTempImages.size() == 21) {
mLastKey = mTempImages.get(0).getNodeKey();
Collections.reverse(mTempImages);
mTempImages.remove(mTempImages.size() - 1);
mImages.addAll(mTempImages);
setAdapter();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
// imagesQuery.addChildEventListener(childEventListener);
// imagesQuery.addValueEventListener(valueEventListener);
imagesQuery.addListenerForSingleValueEvent(valueEventListener);
}
According to the docs
"While using a ChildEventListener is the recommended way to read lists
of data, there are situations where attaching a ValueEventListener to
a list reference is useful.
Attaching a ValueEventListener to a list of data will return the
entire list of data as a single DataSnapshot, which you can then loop
over to access individual children.
Even when there is only a single match for the query, the snapshot is
still a list; it just contains a single item. To access the item, you
need to loop over the result:.
This pattern can be useful when you want to fetch all children of a list in a single operation, rather than listening for additional
onChildAdded events."
I was thinking this will solve the data loading problem but my previous version of the app will still keep using live connection and I'm still seeing random success and failures for data load call in new version of the app with more than 150+ users live right now on old version of the app. What will happen if the old version of the app opens more than 100 connection and the new version of the app tries to load data ? i.e. if 100 connections in the free plan are used will a query with
imagesQuery.addListenerForSingleValueEvent(valueEventListener);
succeed or fail ?
When an Android app first uses the Firebase Database SDK, it makes a connection to the Firebase servers. If there are at that moment already as many connection as are allowed to your database, the new connection will be rejected. The type of listener has no influence on this.
For a lot of discussions covering this already, see this list. Some good ones:
Limitation of free plan in firebase
How the Connection is calculated in Firebase
When are new connections allowed after limit of 100 concurrent connection is reached in firebase?
How exactly are concurrent users determined for a Firebase app?
How to limit concurrent connections on Firebase Android
Having looked at your code. I recommend inserting a closing connection once the read of images from json are completed. In the free package there is a limit of connections so once they read the images, they're technically still connected.
Looking at your Datasnapshot, they don't do anything but still querying the Firebase. I also recommend look into indexing too.
https://firebase.google.com/docs/database/rest/save-data
https://firebase.google.com/docs/database/security/indexing-data
i try to make android app by retrieve temperature and hum data from firebase
json
how to show the data to textview android?
and how to show the latest inserted data from firebase.
thanks
UPDATE
i try this
code
can you give me an example to show the data to android please?
thanks for your help
What you need here is a ValueEventListener. In the fragment/activity that displays your TextView, you can add a ValueEventListener to a firebase reference like this:
// Get reference to firebase location where the data is stored
final Firebase databaseRef = new Firebase(Constants.FIREBASE_URL);
databaseRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d(LOG_TAG,"ValueEventListener:onDataChange: " + "Data has changed");
// Get data
String temperatureData = (String) dataSnapshot.getValue();
// Update UI elements here...
}
#Override
public void onCancelled(FirebaseError firebaseError) {
Log.e(LOG_TAG,"ValueEventListener:onCancelled: " + firebaseError.getMessage());
}
});
Whenever the data at the specified location changes, the onDataChange() callback is triggered, so the UI is always up-to-date. Very powerful.
As someone new to Firebase myself, I can recommend this Udacity course : https://www.udacity.com/course/firebase-essentials-for-android--ud009