Refer coin Firebase database multiples the coins until app crashes - java

I've added refer coin system to my app in which if you refer it to someone you get 100 coins & the person refers code you were using get's the same. Bu the issue is when I run the app and use the referral code coins keep on increasing unless the app crashes. Coins should be added 100 in both accounts but they go from 0 to 24000 or more unless the app crashes.
The following code is :
reference
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
ProfileModel model = snapshot.child(oppositeUID).getValue(ProfileModel.class);
ProfileModel myModel = snapshot.child(user.getUid()).getValue(ProfileModel.class);
int coins = model.getCoins();
int updatedCoins = coins + 100;
int myCoins = myModel.getCoins();
int myUpdate = myCoins + 100;
HashMap<String, Object> map = new HashMap<>();
map.put("coins", updatedCoins);
HashMap<String, Object> myMap = new HashMap<>();
myMap.put("coins", myUpdate);
reference.child(oppositeUID).updateChildren(map);
reference.child(user.getUid()).updateChildren(myMap)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
dialog.dismiss();
Toast.makeText(InviteActivity.this, "Congrats", Toast.LENGTH_SHORT).show();
}
});
}

The problem in your code lies in the fact that you are using addValueEventListener. This means that you are listening for changes in real-time. Every time something changes in your database, the listener fires. Since you are listening to the same location you are performing the updates, the listener fires over and over again. What you need is addListenerForSingleValueEvent, to get the data exactly once.
To solve this, simply change the call to:
addValueEventListener()
To:
addListenerForSingleValueEvent()
And your code will work perfectly fine.

Related

Unexpected behaviour by class variable in android app

In my RegistrationActivity.java class file I've declared a numOfUsers variable to count the number of users in my database.
import ...
public class RegistrationActivity extends AppCompatActivity {
//Other declarations
private static long numOfUsers;
I've created an event listener for checking if a particular user exists in the database. Inside this event listener there is another event listener which counts the total number of users in the database.
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(!dataSnapshot.exists()) {
//create new user
database.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
numOfUsers = dataSnapshot.getChildrenCount();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
//Displaying the numOfUsers in app
userinfo.setText(String.valueOf(numOfUsers));
}
This prints 0. If I place userinfo.setText(String.valueOf(numOfUsers)); inside the second event listener then everything works fine.
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(!dataSnapshot.exists()) {
//create new user
database.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
numOfUsers = dataSnapshot.getChildrenCount();
//This works perfectly fine
userinfo.setText(String.valueOf(numOfUsers));
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
I don't understand why this is happening. numOfUsers is a static class variable I should be able to access it's value from anywhere inside the class. Is there a way I can't print numOfUsers outside the second event listener?
It's not about where you access the data, but about when you access it.
Data is loaded from Firebase (and most modern cloud APIs) asynchronously, since it may take some time to get back.
Instead of blocking your main thread (which would lock out the user), your main code actually continues to run while the data is being loaded. Then when the data is available, your onDataChange gets called with it.
This means that in your code the userinfo.setText(String.valueOf(numOfUsers)) outside of onDataChange runs before onDataChange ever executes and thus passes the wrong value to the text view.
The solution for this is always the same: any code that needs the data from the database must be inside the onDataChange, or be called from there.
This is an incredibly common source of confusion, so I recommend reading more on:
getContactsFromFirebase() method return an empty list
Setting Singleton property value in Firebase Listener
Get the whole values from one key in firebase
Retrieve String out of addValueEventListener Firebase

Firebase posts information incorrectly

I am currently trying to figure out why my Android application is loading information incorrectly into Firebase. The application is posting the information outside of it's location. This is where the information should be:
DriversInformation
-UE7cSOf5Thbum6OwEmg9seAPD463
rates: "0"
But instead, "rates" loads outside like so:
DriversInformation
-UE7cSOf5Thbum6OwEmg9seAPD463
-rates: "0"
In my code, I defined my reference as
driverInformationRef = database.getReference(Common.user_driver_tbl);
Common.user_driver_tbl is a global variable set as
public static final String user_driver_tbl = "DriversInformation";
When I post to Firebase, I am using this code
double finalAverage = averageStars/count;
DecimalFormat df = new DecimalFormat("#.#");
String valueUpdate = df.format(finalAverage);
Map<String,Object> driverUpdateRate = new HashMap<>();
driverUpdateRate.put("rates",valueUpdate);
driverInformationRef.child(Common.driverId)
.updateChildren(driverUpdateRate)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
alertDialog.dismiss();
Toast.makeText(RateActivity.this,"Thank you for your feedback",Toast.LENGTH_SHORT).show();
finish();
}
});
Why is driversInformationRef.child(Common.driverId) not accessing/posting to the correct child?
EDIT: Okay I am genuinely confused. After many recompiling, the rating system started to work. I added this line of code:
Log.d("FINALPROOF", String.valueOf(Common.driverId));
Why does this line of code make such a difference? Does this initilize the value in some way?

Why this method runs before the other method finishes?

I am kinda new to Android programming. I am building a simple application and I have a "follow-unfollow" concept on it. What I simply want is, if the current user follows the user he/she is exploring, I want him/her to see "unfollow" button. If not following, there should be a "follow" button. On my UserProfileActivity class I have a method called onPrepareOptionsMenu() and inside this method I can set the buttons.
#Override
public boolean onPrepareOptionsMenu(Menu menu) {
MenuItem follow = menu.findItem(R.id.action_follow);
MenuItem unfollow = menu.findItem(R.id.action_unfollow);
Bundle bundle = this.getIntent().getExtras();
if(isFollowing(bundle.getString("userid")) == true){
follow.setVisible(false);
unfollow.setVisible(true);
}
else{
follow.setVisible(true);
unfollow.setVisible(false);
}
return super.onPrepareOptionsMenu(menu);
}
Also, I have another method called isFollowing() and it returns a boolean "true" if current user follows the other user, it returns "false" if not. It is the simplest way that I have thought to solve this issue.
public boolean isFollowing(String userID){
isFollowingResult = false;
firebaseDatabase = FirebaseDatabase.getInstance();
databaseReference = firebaseDatabase.getReference();
final DatabaseReference followingData = databaseReference.child("followingData");
followingData.child(currentUser.getUid()).child(userID).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists())
isFollowingResult = true;
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return isFollowingResult;
}
When I run this, and when I click on a user's profile onPrepareOptionsMenu() method is called and inside it isFollowing() method is called. The problem is, it does not wait for the isFollowing() method to run and finish running and it immediately sees it as "false" and always shows "follow" button. Any suggestion would be appreciated.
Thanks.
Make follow and unfollow the fields of the class. Then change their visibility in onDataChange(DataSnapshot) method.
There are multiple issues with the code.
Firstly, your isFollowing() function is setting up the listener, on the data field, but it'll only get called when the data changes. In this case, you may only want to read the data once:
https://firebase.google.com/docs/database/android/read-and-write#read_data_once
Secondly, the use of a listener implies asynchronicity. Meaning, you'll need to wait until you get the callback later in order to get the value you want.
The ideal solution in order to maintain responsiveness of your app is to maintain a local "copy" of the value in your database with a listener that constantly updates that value. That way, you can query the state of your variable quickly (since it's stored/replicated locally) and still be up to date with your database (with the listener).
This will also prevent each "read" from going all the way to the service and back and also remove the need for your UI to wait to render correctly (accurately).

addValueChangeListener doen't trigger in android

I have a app in building proccess in some where i need to get data from FirebaseDatabase and show them in custom list view here my code part of it for onDataChange method
myDatabase=FirebaseDatabase.getInstance();
myRef= myDatabase.getReference().child("TvSeries");
myAuth = FirebaseAuth.getInstance();
myUser = myAuth.getCurrentUser();
myRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot currentData : dataSnapshot.getChildren()){
if(currentData.child("tCategory").child("tPrimary").getValue().toString().equals("Aksiyon")){ }
selectedCategoryList.add(new DataForRow(currentData.getKey(),
currentData.child("tCategory").child("tPrimary").getValue().toString(),
currentData.child("tReleaseDate").getValue().toString()));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Integer size =selectedCategoryList.size();
Log.d("Size:", size.toString());
When i put in breakpoint onDataChange method it works but otherwise it didnt any suggestion is very helpful. Have a nice day all.
Your selectedCategoryList list is always empty because onDataChange() method has an asynchronous behaviour which means that is called even before you are try to add those objects of DataForRow class to the list. A quick solve for this problem would be to declare and use your selectedCategoryList list only inside the onDataChange() method or if you want to use it outside, you need to create your own callback and for that, I recommend you see the last part of my answer from this post.
Firebase works asynchronously. You probably got the data from firebase after you program executed the line with Log. As Tristan mentioned, if you put your Log inside of the listener, it will work

Print out only finished memberList

so the Code looks like this right now.
The Problem is that I get more than one Logprints. But I need only the full and finished meberList.
for (String memberByNumber : memberListByNumber) {
mFirebaseDatabaseReference.child("userUidsByNumber/").child(memberByNumber).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
String userUid = dataSnapshot.getValue().toString();
memberList.add(userUid);
Log.d("LOL", "ContactsAdapter: " + memberList);
} else {
Snackbar.make(view, R.string.user_does_not_exist, Snackbar.LENGTH_LONG).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Snackbar.make(view, R.string.error, Snackbar.LENGTH_LONG).show();
}
});
}
I hope you understand my Problem and can help me.
Thanks in advance.
If onDataChange gets called for every member for memberListByNumber, then you can try
final int total = memberListByNumber.size();
for (String memberByNumber : memberListByNumber) {
mFirebaseDatabaseReference
.child("userUidsByNumber/")
.child(memberByNumber)
.addValueEventListener(new ValueEventListener() {
int n = total;
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
String userUid = dataSnapshot.getValue().toString();
memberList.add(userUid);
if (--n <= 0) Log.d("LOL", "ContactsAdapter: " + memberList);
} else {
Snackbar.make(view, R.string.user_does_not_exist, Snackbar.LENGTH_LONG).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Snackbar.make(view, R.string.error, Snackbar.LENGTH_LONG).show();
}
});
}
I want to comment it but due to my reputation here I couldn't
So, here is my answer your logging every entry into one file so that it just makes printing the necessary entries much harder So go on different path and give one more if-else statement in existing one and use try-exception to divide the finished and canceled into some cache memory and then void the finished file
In firebase, there's never a thing as complete list.
Value events keep happening whenever any data changes under any of the child nodes. Also value events happen last ie after all child events are finished ensuring all children nodes at that point are present. Next event will give you the complete list at that particular point.
On each data change call, you can clear the list, add all the new values and print that. This is how firebase works.
If you want the entire list only once and don't want any new changes, you can add the value event listeners using addListenerForSingleValueEvent method. Now the list will be read once and no more. However if you have persistence enabled, the data will be read once from the phone cache and not from the db
Maybe what you are looking for is a ChildEventListener which will give you more granular control. This will have child added, deleted and changed events which will give you the list data one child at a time.
I don't know what your actual purpose is so this is the best i can try to explain. Hope this helps

Categories