Android beginner here. This may sound very silly, but I'm having issues with an if-else block that I created in my onDataChange() method in an event listener for a firebase database-reference.
Here's the code for the listener:
requestRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()){
for(DataSnapshot request : dataSnapshot.getChildren()){
RequestDetails retrievedDetails = request.getValue(RequestDetails.class);
if(retrievedDetails.equals(requestDetails)){
alreadyRequested = true;
// finish();
// startActivity(getIntent());
break;
}
}
}
if(!alreadyRequested){
//alreadyDisplayed = true;
mDatabase.child("Requests").push().setValue(requestDetailsHashMap);
Toast.makeText(ParticipantSportOptions.this, "Request Successfully Sent!", Toast.LENGTH_SHORT).show();
// finish();
// startActivity(getIntent());
}
else if(alreadyRequested){
Toast.makeText(ParticipantSportOptions.this, "Request has already been received!\nPlease wait for approval!",
Toast.LENGTH_SHORT).show();
return;
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
This listener is inside the overridden method onClick() for a button.
What needs to happen is this - the first time this button is clicked, data (a request) is written into the database. For every subsequent click of the button, I check if the user is attempting to send multiple requests and if so I display the toast and return.
What I observe during run-time is that on the first click of the button, both toast messages are displayed.
Why does this happen?
As said by the_noob it can be fixed by using addListenerForSingleValueEvent()
Related
I am using addvalueEventListener function to fetch data from firebase. I am trying to achieve AND operation by using this function. Issue is that when I am calling this function, it can filter data client side and also show in recyclerview but it can run if statement if data found and also run else part.
Suppose I want data which is available in firebase, it filters the data and show and also run else part data not found.
My main function code is:
private void GetFilterData(final String filter) {
reference = FirebaseDatabase.getInstance().getReference().child("Donation");
Query query = reference.limitToLast(20).orderByChild("username").equalTo(username);
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
//Here I can check if data is exist or not, if exist it shows data
if (dataSnapshot.exists())
{
arrayList.clear();
alert.setVisibility(View.GONE);
for (DataSnapshot dataSnapshot1:dataSnapshot.getChildren())
{
DonationHelper donationHelper = dataSnapshot1.getValue(DonationHelper.class);
//here I want to check again and filter data, if data exist, shows data and if does not exist required filter then run else part. But issue is that if data exist it shows data and also run its else part.
if (donationHelper.getAction().equals(filter))
{
pd.cancel();
arrayList.add(donationHelper);
}
else
{
//Filter data exist or not, everytime it run else part code
pd.cancel();
alert.setVisibility(View.VISIBLE);
alert.setText("Currently no request is in Queue");
//Toast.makeText(Track_Donation.this, "No data found", Toast.LENGTH_SHORT).show();
}
adapter = new DonationAdapter(Track_Donation.this,arrayList);
donationdata.setAdapter(adapter);
}
}
else
{
pd.cancel();
alert.setText("Start do Donation, and help Poor and Needy people's");
Toasty.error(Track_Donation.this, "You don't do any Donation yet", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
pd.cancel();
Toasty.error(Track_Donation.this, "Server Error", Toast.LENGTH_SHORT).show();
}
});
}
I have a firebase database from which I save and retrieve data from, to and from. I know how datasnapshot works inside an addValueEventListener. The problem is that this is only called or triggered when the firebase database detects change in its data. I only want to access data and read it to be able to store it in an arraylist or the same thing.
I have a code like this:
public void foo(){
DatabaseReference x= FirebaseDatabase.getInstance().getReference().child("x");
reservations.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String userID = client.getId();
for(DataSnapshot snap : dataSnapshot.getChildren()){
if(snap.child("someId").equals(someId)) number++;
if(snap.child("userID").getValue().equals(client.getId())){
isAlreadyReserved = true; // if user has already reserved the item
alreadyReserved();
break;
}
Log.e("isAlreadyReserved: ", isAlreadyReserved+"");
numberOfReservations++;
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
if(isAlreadyReserved) {
alreadyReserved();
}
else if(number == numberOfCopies){
// material is no longer available
OtherActivity.showMaterialUnavailable();
}
else{
Reservation reservation = new Reservation();
reservation.setBookId(this.bookId);
reservation.setResID((numberOfReservations+1)+"");
reservation.setUserID(client.getId());
String key = reservations.push().getKey();
reservations.child(key).setValue(reservation);
Log.e("Reservations: ", "reserve successful");
AlertDialog.Builder builder = new AlertDialog.Builder(this.context);
builder.setTitle(R.string.reservationSuccess_title)
.setMessage(R.string.reservationSuccess_body);
AlertDialog dialog = builder.create();
dialog.show();
}
}
You can see that inside onDataChange I only count materials and set some flags, which I can supposedly do outside the ValueEventListener.
But I notice that this is faulty because onDataChange is called only when writing to the Firebase database occurs. Which should not be the case.
What can I do to loop through the values inside the DatabaseReference x without calling onDataChange, or without using DataSnapshot?
You cannot loop inside a DatabaseReference without using a listener. When we are talking about Firebase, we are talking only about listeners. So in order to get those values, you need to use a listener and than get the data out from the dataSnapshot.
What i think your problem is in your case, is that onDataChange method is called in an asynchronously way. This means that everything you are doing outsite this method is actually executed before onDataChange method has been called. So in order to understand what is actually going on, please see this post and this post. Reading this posts, will teach you how to query data in Firebase and how to retrieve data from Firebase asynchronously.
Hope it helps.
In order to get the values of DatabaseReference x, you should use addListenerForSingleValueEvent
x.addListenerForSingleValueEvent(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
//do something
}
#Override
public void onCancelled(DatabaseError databaseError)
{
//do something
}
});
as mentioned in the firebase documentation:
public void addListenerForSingleValueEvent (ValueEventListener
listener)
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.
This is my code for reading Firebase data:
final String[] booknum = {"0"};
databaseReference.child("All BID").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
booknum[0] =Long.toString(snapshot.getChildrenCount());
Toast.makeText(getApplicationContext(), booknum[0],Toast.LENGTH_LONG).show();
}
#Override public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
}
});
Toast.makeText(getApplicationContext(), booknum[0],Toast.LENGTH_LONG).show();
When I am executing this, the first toast(inside ValueEventListener) prints the right answer (eg '8'). But the toast outside always prints 0 no matter what.
Please Help!
The ValueEventListener is asynchronous. The lines of code inside of the listener, including the assignment of booknum[0], are not guaranteed to execute before the lines of code written outside of the listener. If you depend on the new value of booknum[0] for some operation, consider moving that operation inside of onDataChange() to ensure it uses the new value.
I'm totally new to Firebase and need to know how to check if my writing task was successful because if I don't, the MainActivity starts and messes up my Register progress.
This checks if Username is already taken and registers the user if it isn't:
Query usernamequery = myRef.orderByChild("Username").equalTo(Username);
usernamequery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.exists()) {
// TODO: handle the case where the data already exists
editText.setError("Username taken!");
return;
}
else {
// TODO: handle the case where the data does not yet exist
myRef.child("Users").child(Username).child("Userid").setValue(user.getUid());
myRef.child("Users").child(Username).child("Username").setValue(Username);
startActivity(maps);
finish();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Toast.makeText(Username.this, "Error", Toast.LENGTH_LONG).show();
}
});
But I want the Intent to Main Activity (maps) only be fired when
myRef.child("Users").child(Username).child("Userid").setValue(user.getUid());
and the other one is finished with its task and is successful.
What can I do?
To know when a write operation has completed on the server, add a completion listener:
myRef.child("Users").child(Username).child("Userid").setValue(user.getUid(), new DatabaseReference.CompletionListener() {
void onComplete(DatabaseError error, DatabaseReference ref) {
System.err.println("Value was set. Error = "+error);
// Or: throw error.toException();
}
});
If there was an error, details will be in the error operation. If the write operation was completed without problems, the error will be null.
If you want to write to multiple locations with a single operation, you'll want to look at the update() method
The correct overload for setValue() is documented here.
I am working with Firebase for my Android app. I have successfully retrieved information in multiple places in my code using something like:
dbRef = new Firebase(Constants.URL_DB);
childRef = dbRef.child(Constants.KEY_CHILD);
objectRef = childRef.child(objectId);
// Log the URL to the object.
// This has been verified. The path is good.
Log.d("DEBUG", "To String: " + objectRef.toString());
childRef.addValueEventListener(new ValueEventListener()
#Override
public void onDataChange(DataSnapshot snapshot) {
Log.d("DEBUG", "Success");
}
#Override
public void onCancelled(FirebaseError firebaseError) {
Log.e("DEBUG", "Failure");
}
});
The exact same code has been use successfully in another activity but for some reason, it doesn't work in on particular activity (onDataChange is never called, neither is onCancelled). Could it be that The Firebase sessions in other activities are conflicting with this one? I have seen problems that could be resolved with:
Firebase.goOnline();
Firebase.goOffline();
Althought I am not sure I understand what those exactly do. Maybe it is because I somehow can't access a child added by push() with his id?
UPDATE:
I successfully wrote in the database at objectRef, but onDataChange() is still never called.
Ok I found what was causing the problem. I had an empty loop because I needed the information to continue and it somehow prevented the events from the database. I had something like this in the onCreate():
childRef.addValueEventListener(new ValueEventListener()
#Override
public void onDataChange(DataSnapshot snapshot) {
Log.d("DEBUG", "Success");
object = snapshot.getValue(MyObject.class);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
Log.e("DEBUG", "Failure");
}
});
And I had the loop in the onResume():
while(object == null);
What I did to correct the issue was replacing the loop with:
if(object == null){
// Do stuff
}
And I added a call to onResume() in onDataChange()