Pagination not working using Firebase database - java

I have a problem with firebase pagination.
I have table posts and this is an example structure:
and I want to get only 10 post every time, here is my code page is 0:
#NonNull
#CheckResult
public Single<DataSnapshot> getData(#NonNull DatabaseReference ref, int page) {
return Single.create(emitter -> {
ref.orderByChild("timestamp")
.startAt(page * 10)
.limitToFirst(10);
final ValueEventListener listener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (!emitter.isDisposed()) {
emitter.onSuccess(dataSnapshot);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
if (!emitter.isDisposed()) {
emitter.onError(databaseError.toException());
}
}
};
ref.addListenerForSingleValueEvent(listener);
});
}
and here is the result
Why I have list size equals 20 not 10? p.s. limit to first or limit to last it's no difference with the result

Calling startAt(), limitToFirst() and similar methods on a DatabaseReference returns a new Query object. You need to keep a reference to that Query and attach your listeners to that:
Query query = ref.orderByChild("timestamp")
.startAt(page * 10)
.limitToFirst(10);
query.addListenerForSingleValueEvent(listener);

Related

MPAndroidChart not able to create bars based on Realtime Database Values [duplicate]

public List<String> getContactsFromFirebase(){
FirebaseDatabase.getInstance().getReference().child("Users")
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Users user = snapshot.getValue(Users.class);
assert user != null;
String contact_found = user.getPhone_number();
mContactsFromFirebase.add(contact_found);
Log.i("Test", mContactsFromFirebase.toString());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return mContactsFromFirebase;
}
I can't seem to find the error. In the code above, when I call the log, I get the values from mContactsFromFirebase, but the getContactsFromFirebase() method return an empty list. Could you help me please?
Data is loaded from Firebase asynchronously. Since it may take some time to get the data from the server, the main Android code continues and Firebase calls your onDataChange when the data is available.
This means that by the time you return mContactsFromFirebase it is still empty. The easiest way to see this is by placing a few log statements:
System.out.println("Before attaching listener");
FirebaseDatabase.getInstance().getReference().child("Users")
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println("In onDataChange");
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
System.out.println("After attaching listener");
When you run this code, it will print:
Before attaching listener
After attaching listener
In onDataChange
That is probably not the order that you expected the output in. As you can see the line after the callback gets called before onDataChange. That explains why the list you return is empty, or (more correctly) it is empty when you return it and only gets filled later.
There are a few ways of dealing with this asynchronous loading.
The simplest to explain is to put all code that returns the list into the onDataChange method. That means that this code is only execute after the data has been loaded. In its simplest form:
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Users user = snapshot.getValue(Users.class);
assert user != null;
String contact_found = user.getPhone_number();
mContactsFromFirebase.add(contact_found);
System.out.println("Loaded "+mContactsFromFirebase.size()+" contacts");
}
}
But there are more approaches including using a custom callback (similar to Firebase's own ValueEventListener):
Java:
public interface UserListCallback {
void onCallback(List<Users> value);
}
Kotlin:
interface UserListCallback {
fun onCallback(value:List<Users>)
}
Now you can pass in an implementation of this interface to your getContactsFromFirebase method:
Java:
public void getContactsFromFirebase(final UserListCallback myCallback) {
databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Users user = snapshot.getValue(Users.class);
assert user != null;
String contact_found = user.getPhone_number();
mContactsFromFirebase.add(contact_found);
System.out.println("Loaded "+mContactsFromFirebase.size()+" contacts");
}
myCallback.onCallback(mContactsFromFirebase);
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
});
}
Kotlin:
fun getContactsFromFirebase(myCallback:UserListCallback) {
databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(object:ValueEventListener() {
fun onDataChange(dataSnapshot:DataSnapshot) {
for (snapshot in dataSnapshot.getChildren())
{
val user = snapshot.getValue(Users::class.java)
assert(user != null)
val contact_found = user.getPhone_number()
mContactsFromFirebase.add(contact_found)
System.out.println("Loaded " + mContactsFromFirebase.size() + " contacts")
}
myCallback.onCallback(mContactsFromFirebase)
}
fun onCancelled(databaseError:DatabaseError) {
throw databaseError.toException()
}
})
And then call it like this:
Java:
getContactsFromFirebase(new UserListCallback() {
#Override
public void onCallback(List<Users> users) {
System.out.println("Loaded "+users.size()+" contacts")
}
});
Kotlin:
getContactsFromFirebase(object:UserListCallback() {
fun onCallback(users:List<Users>) {
System.out.println("Loaded " + users.size() + " contacts")
}
})
It's not as simple as when data is loaded synchronously, but this has the advantage that it runs without blocking your main thread.
This topic has been discussed a lot before, so I recommend you check out some of these questions too:
this blog post from Doug
Setting Singleton property value in Firebase Listener (where I explained how in some cases you can get synchronous data loading, but usually can't)
return an object Android (the first time I used the log statements to explain what's going on)
Is it possible to synchronously load data from Firebase?
https://stackoverflow.com/a/38188683 (where Doug shows a cool-but-complex way of using the Task API with Firebase Database)
How to return DataSnapshot value as a result of a method? (from where I borrowed some of the callback syntax)

Data drawn from Firebase Database cannot be stored in a variable even when drawn successfully [duplicate]

public List<String> getContactsFromFirebase(){
FirebaseDatabase.getInstance().getReference().child("Users")
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Users user = snapshot.getValue(Users.class);
assert user != null;
String contact_found = user.getPhone_number();
mContactsFromFirebase.add(contact_found);
Log.i("Test", mContactsFromFirebase.toString());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return mContactsFromFirebase;
}
I can't seem to find the error. In the code above, when I call the log, I get the values from mContactsFromFirebase, but the getContactsFromFirebase() method return an empty list. Could you help me please?
Data is loaded from Firebase asynchronously. Since it may take some time to get the data from the server, the main Android code continues and Firebase calls your onDataChange when the data is available.
This means that by the time you return mContactsFromFirebase it is still empty. The easiest way to see this is by placing a few log statements:
System.out.println("Before attaching listener");
FirebaseDatabase.getInstance().getReference().child("Users")
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println("In onDataChange");
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
System.out.println("After attaching listener");
When you run this code, it will print:
Before attaching listener
After attaching listener
In onDataChange
That is probably not the order that you expected the output in. As you can see the line after the callback gets called before onDataChange. That explains why the list you return is empty, or (more correctly) it is empty when you return it and only gets filled later.
There are a few ways of dealing with this asynchronous loading.
The simplest to explain is to put all code that returns the list into the onDataChange method. That means that this code is only execute after the data has been loaded. In its simplest form:
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Users user = snapshot.getValue(Users.class);
assert user != null;
String contact_found = user.getPhone_number();
mContactsFromFirebase.add(contact_found);
System.out.println("Loaded "+mContactsFromFirebase.size()+" contacts");
}
}
But there are more approaches including using a custom callback (similar to Firebase's own ValueEventListener):
Java:
public interface UserListCallback {
void onCallback(List<Users> value);
}
Kotlin:
interface UserListCallback {
fun onCallback(value:List<Users>)
}
Now you can pass in an implementation of this interface to your getContactsFromFirebase method:
Java:
public void getContactsFromFirebase(final UserListCallback myCallback) {
databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Users user = snapshot.getValue(Users.class);
assert user != null;
String contact_found = user.getPhone_number();
mContactsFromFirebase.add(contact_found);
System.out.println("Loaded "+mContactsFromFirebase.size()+" contacts");
}
myCallback.onCallback(mContactsFromFirebase);
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
});
}
Kotlin:
fun getContactsFromFirebase(myCallback:UserListCallback) {
databaseReference.child(String.format("users/%s/name", uid)).addListenerForSingleValueEvent(object:ValueEventListener() {
fun onDataChange(dataSnapshot:DataSnapshot) {
for (snapshot in dataSnapshot.getChildren())
{
val user = snapshot.getValue(Users::class.java)
assert(user != null)
val contact_found = user.getPhone_number()
mContactsFromFirebase.add(contact_found)
System.out.println("Loaded " + mContactsFromFirebase.size() + " contacts")
}
myCallback.onCallback(mContactsFromFirebase)
}
fun onCancelled(databaseError:DatabaseError) {
throw databaseError.toException()
}
})
And then call it like this:
Java:
getContactsFromFirebase(new UserListCallback() {
#Override
public void onCallback(List<Users> users) {
System.out.println("Loaded "+users.size()+" contacts")
}
});
Kotlin:
getContactsFromFirebase(object:UserListCallback() {
fun onCallback(users:List<Users>) {
System.out.println("Loaded " + users.size() + " contacts")
}
})
It's not as simple as when data is loaded synchronously, but this has the advantage that it runs without blocking your main thread.
This topic has been discussed a lot before, so I recommend you check out some of these questions too:
this blog post from Doug
Setting Singleton property value in Firebase Listener (where I explained how in some cases you can get synchronous data loading, but usually can't)
return an object Android (the first time I used the log statements to explain what's going on)
Is it possible to synchronously load data from Firebase?
https://stackoverflow.com/a/38188683 (where Doug shows a cool-but-complex way of using the Task API with Firebase Database)
How to return DataSnapshot value as a result of a method? (from where I borrowed some of the callback syntax)

firebase dataBase read issue

I'm trying to get value from the last node on firebase Datasebase
I've seen a few solutions but none of them worked for me.
The code is given below:
setData = FirebaseDatabase.getInstance().getReference().child("Questions");
Query lastQuery = setData.orderByKey().limitToLast(1);
lastQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChild("JSON_OBJ")) {
SubmitKey item = dataSnapshot.getValue(SubmitKey.class);
String key2 = item.getJSON_OBJ();
} else {
Snackbar.make(findViewById(R.id.QuestionLayout), "JSON_ObJ not found", Snackbar.LENGTH_LONG).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
My Constructor Class
public class SubmitKey {
private String JSON_OBJ;
public SubmitKey() {
}
public SubmitKey(String JSON_OBJ) {
this.JSON_OBJ = JSON_OBJ;
}
public String getJSON_OBJ() {
return JSON_OBJ;
}
}
instead of getting the value it is going to the else statement and showing 'JSON_OBJ not Found'.
Any help would be appreciated.
Edit: I've made some changes in the code and now it is showing no setter/field found for 0-101 found on class submitKey
D/ViewRootImpl#5b51511[Questions]: ViewPostIme pointer 0
D/ViewRootImpl#5b51511[Questions]: ViewPostIme pointer 1
And it is aslo not going inside if statement goes to else statement
When you execute a query against the Firebase Database, there will potentially be multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result.
Your onDataChange will need to take care of this list, by iterating over snapshot.getChildren():
lastQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot dataSnapshot: snapshot.getChildren()) {
if (dataSnapshot.hasChild("JSON_OBJ")) {
SubmitKey item = dataSnapshot.getValue(SubmitKey.class);
String key2 = item.getJSON_OBJ();
} else {
Snackbar.make(findViewById(R.id.QuestionLayout), "JSON_ObJ not found", Snackbar.LENGTH_LONG).show();
}
}
}

Firebase Database for Android, trouble retrieving data

So I'm having trouble with retrieving data from my database. I think the problem lies in the return statement.
When I run the app the debug text where the flatID should be shown is blank. I know for sure that the data is on the database so that isn't the issue.
I'm still very new to Java and programming in general, thanks for your help and your patience :) The code is as follows.
flatID = readFlatID();
debug1.setText(flatID);
public String readFlatID(){
String uid = mAuth.getCurrentUser().getUid().toString();
mDatabase.child("Users").child(uid).child("Flat")
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
flat = dataSnapshot.getValue().toString();
Toast.makeText(getApplicationContext(),"Data Read Successfully",Toast.LENGTH_SHORT).show();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return flat;
}
Firebase methods are Asynchronous. That's why the text was not set. You can set up a listener and pass that to the method such that you get a callback when the data is fetched. Here is what I mean code-wise:
//Create an interface
interface IDatabaseLoad {
void onDataLoadSuccess(String data);
void onDataLoadFailed();
}
//Initialize it
IDatabaseLoad databaseLoad = new IDatabaseLoad() {
#Override
public void onDataLoadSuccess(String data) {
debug1.setText(data);
Toast.makeText(getApplicationContext(),"Data Read Successfully",Toast.LENGTH_SHORT).show();
}
#Override
public void onDataLoadFailed() {
}
};
//modify your method
public void readFlatID(final IDatabaseLoad listener){
String uid = mAuth.getCurrentUser().getUid().toString();
mDatabase.child("Users").child(uid).child("Flat")
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
listener.onDataLoadSuccess(dataSnapshot.getValue().toString());
}
#Override
public void onCancelled(DatabaseError databaseError) {
listener.onDataLoadFailed();
}
});
}
Now you just need to call readFlatID(databaseLoad) and the TextView will be set with the data after it gets fetched.
As fetching data from Firebase server is performed on the background thread on mobile devices, it takes time for the app to actually load the whole data. That is why return flat; will return nothing at the time the code is executed.
The simplest solution for you is, you have to put all what you want to do with the retrieved data inside the onDataChange() method. You can do something like this:
public void readFlatID() {
String uid = mAuth.getCurrentUser().getUid().toString();
mDatabase.child("Users").child(uid).child("Flat")
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
flat = dataSnapshot.getValue().toString();
debug1.setText(flat);
Toast.makeText(getApplicationContext(), "Data Read Successfully", Toast.LENGTH_SHORT).show();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
You cannot return something now that hasn't been loaded yet. The onDataChange() method has an asynchronous behaviour which means that is called even before you are trying to get the flat object from the database. That's why it is always null outside onDataChange() method. With other words, by the time you are returning the flat object, the data has not finished loading yet from the database, so a quick solve for this would be to use the value only inside the onDataChange() method or if you want to use it outside, dive into the asynchronous world and create your own callback as explained in the last part of my answer from this post.

Check if ID exists in Firebase Android

I am writing an android app and I want to check if a key exists in order to avoid duplicate values. I´ve been investigating but it looks that all I can add is listeners, when I just want to check if an ID exists or not already.
Taking this SO question as an example, I would like to know if -JlvccKbEAyoLL9dc9_v exists. How can I do this?
Thanks in advance.
The approach will always be similar to what I wrote in this answer about JavaScript: Test if a data exist in Firebase
ref.child("-JlvccKbEAyoLL9dc9_v").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.exists()) {
// TODO: handle the case where the data already exists
}
else {
// TODO: handle the case where the data does not yet exist
}
}
#Override
public void onCancelled(FirebaseError firebaseError) { }
});
But keep in mind that push ids in Firebase exist to prevent having to do this sort of check. When multiple clients generate push ids, they are statistically guaranteed to be unique. So there's no way one of them can create the same key as another.
Any case where you need to check if an item already exists is likely to have race conditions: if two clients perform this check almost at the same time, neither of them will find a value.
RxJava 2 :
public static Observable<Boolean> observeExistsSingle(final DatabaseReference ref) {
return Observable.create(emitter ->
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
emitter.onNext(dataSnapshot.exists());
emitter.onComplete();
}
#Override
public void onCancelled(DatabaseError databaseError) {
emitter.onError(databaseError.toException());
}
}));
}
Usage:
public Observable<Boolean> isYourObjectExists(String uid) {
return observeExistsSingle(databaseReference.child(uid));
}
In your class:
yourRepo.isYourObjectExists("-JlvccKbEAyoLL9dc9_v")
.subscribe(isExists -> {}, Throwable::printStackTrace);
Based on #Frank van Puffelen's answer, here's a few lines to see if a ref - itself - exists before using it.
public void saveIfRefIsAbsent(DatabaseReference firebaseRef) {
DatabaseReference parentRef = firebaseRef.getParent();
String refString = firebaseRef.toString();
int lastSlashIndex = refString.lastIndexOf('/');
String refKey = refString.substring(lastSlashIndex + 1);
parentRef.child(refKey).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.exists()) {
// TODO: handle the case where the data already exists
}
else {
// TODO: handle the case where the data does not yet exist
}
}
#Override
public void onCancelled(FirebaseError firebaseError) { }
});
}
In my case I have a Util to create the schema programmatically. I use this in order to add new data without overwriting existing data.

Categories