This question already has an answer here:
getContactsFromFirebase() method return an empty list
(1 answer)
Closed 4 years ago.
I have a boolean set up to check for duplicate usernames in an app. I want the boolean to return data based on the result of an onDataChange in a ValueEventListener. Here's what I have:
private boolean isUsernameValid(final String username) {
mReference = FirebaseDatabase.getInstance().getReference();
Query query = mReference.child("users").child(username);
ValueEventListener mListener = new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
//create condition that would make isUsernameValid return false
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
};
mReference.addListenerForSingleValueEvent(mListener);
return //if that condition is not met;
}
If you try to do something like this, you will always be returning false, since Firebase is asynchronous, you will need to wait a little bit depending on your connection in order to return any value from dataSnapshot()
private boolean isUsernameValid(final String username) {
boolean isUsernameValid;
mReference = FirebaseDatabase.getInstance().getReference();
Query query = mReference.child("users").child(username);
ValueEventListener mListener = new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
//create condition that would make isUsernameValid return false
isUsernameValid = true;
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
};
mReference.addListenerForSingleValueEvent(mListener);
return isUsernameValid //if that condition is not met;
}
To solve this issue you can create an interface that will fires right when we know we have the results from onDataChange() and then you can return anything from there
first create an interface
public interface FirebaseSuccessListener {
void onDataFound(boolean isDataFetched);
}
Then we just do your method that checks for the user if exists
private void isUsernameValid(final String username, FirebaseSuccessListener dataFetched) {
mReference = FirebaseDatabase.getInstance().getReference();
Query query = mReference.child("users").child(username);
ValueEventListener mListener = new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
//here we are telling to our interface that the data has been found and then we can do anything with it outside this method
dataFetched.onDataFound(true);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
};
mReference.addListenerForSingleValueEvent(mListener);
}
And then we can call the method in order to get your value from inside onDataChange()
isUsernameValid.(new FirebaseSuccessListener() {
#Override
public void onCallback(boolean isDataFetched) {
if(isDataFetched){
//you know the value is true, here you can update or request any change for example you can do this
userIsOnFirebase(true);
}else{
userIsOnFirebase(false);
}
}
});
private boolean userIsOnFirebase(boolean isOnFirebase){
return isOnFirebase;
}
then use this method above to return if the user is or not in your db. Make sure when you call userIsOnFirebase since it will return false if the data is still not fetched
Related
This question already has an answer here:
getContactsFromFirebase() method return an empty list
(1 answer)
Closed 3 years ago.
private Firebase_Database DbOnline;
ArrayList<ClassModel> clsList;
clsList = DbOnline.getClassesList();//return arraylist containing objects ...
//Implementation of getClassesList() in Firebase_Database CLASS..
public ArrayList<ClassModel> getClassesList(){//upto to this every thing execute but from here the //execution jumps to if(condition) line below...and I get null arraylist in return
FbDb.child("Classes").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot ds: dataSnapshot.getChildren())
{
ClassModel classModel = ds.getValue(ClassModel.class);
classModels.add(classModel);
Log.i("Tag", "Msg");
}
Log.i("Tag", String.valueOf(classModels.size()));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
if (classModels==null){
Log.i("TAG","Null NO DATA IN DATABASE");
}
return classModels;
}
Operation to firebase is asynchronous. so you have to wait to get data. You can use LiveData and observe it to get updated content. Check below:
private MutableLiveData<ArrayList<ClassModel>> mutableClassModels = new MutableLiveData<>();
private ArrayList<ClassModel> classModels = new ArrayList<>();
public MutableLiveData<ArrayList<ClassModel>> getClassesList(){//upto to this every thing execute but from here the //execution jumps to if(condition) line below...and I get null arraylist in return
FbDb.child("Classes").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot ds: dataSnapshot.getChildren())
{
ClassModel classModel = ds.getValue(ClassModel.class);
classModels.add(classModel);
Log.i("Tag", "Msg");
}
mutableClassModels.postValue(classModels);
Log.i("Tag", String.valueOf(classModels.size()));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
return mutableClassModels;
}
And then observe it like below:
DbOnline.getClassesList().observe(this, new Observer<ArrayList<ClassModel>>() {
#Override
public void onChanged(ArrayList<ClassModel> classModels) {
// Do your operation here
}
});
Update:
Update your adapter like below:
//Initialize it, as it causing NullPointerException
ArrayList<ClassModel> clsList = new ArrayList<>();
public Adapter(Context context, String name) {
...
DbOnline=new Firebase_Database();
if (fragName.equals(listForClasses)) {
DbOnline.getClassesList().observe((LifecycleOwner) context, new Observer<ArrayList<ClassModel>>() {
#Override
public void onChanged(ArrayList<ClassModel> classModels) {
clsList =classModels;
clsList.size();
//Notify to refresh the items
notifyDataSetChanged();
}
});
} else {
sList = null;//DbOffline.getStudentsList("");
}
}
This question already has answers here:
How to return DataSnapshot value as a result of a method?
(6 answers)
Closed 4 years ago.
before reading I'm a newbie trying to learn android development so don't go hard on me, please.
I have this Realtime database firebase :
I'm trying to check if the entered email exists , i've tried a method but it did not work out for me.
I tried this method :
private boolean checkIfExistes() {
reference.child("Users").orderByChild("email").equalTo(Email.getText().toString()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists())
CheckExists =true;
else
CheckExists =false;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
if (CheckExists)
return true;
else
return false;
}
it always returns false though even if the email exists. Help please.
Try this:
boolean CheckExists =false; //declare and assign default value in global scope
reference.child("Users").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
Iterable<DataSnapshot> userChildren = dataSnapshot.getChildren();
for (DataSnapshot user: userChildren) {
User u = user.getValue(User.class); //make a model User with necessary fields
if(u.email.equalsIgnoreCase(Email.getText().toString())){
CheckExists =true;
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
private DatabaseReference rootRef,userRef;
get the initialize firebase in oncreare method
rootRef = FirebaseDatabase.getInstance().getReference();
userRef = rootRef.child("Users");
here the code for checking email exist or not
userRef.orderByChild("email").equalTo(emailEdt.getText().toString())
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
hideProgress();
if (dataSnapshot.exists()){
Toast.makeText(LoginActivity.this,"User already exists",Toast.LENGTH_SHORT).show();
}else {
UserLogin user = new UserLogin(emailEdt.getText().toString(),profilePath);
DatabaseReference db = userRef.push();
db.setValue(user);
//Log.d(TAG,"user key is::: "+db.getKey());
prefs.savEmail(emailEdt.getText().toString());
prefs.savProfileUrl(profilePath);
prefs.savKey(db.getKey());
openMainActivity();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
hideProgress();
Toast.makeText(LoginActivity.this,databaseError.getMessage(),Toast.LENGTH_SHORT).show();
}
});
This is my database. I want to check if a particular uid exists before adding so that I can avoid duplicate entries of the same uid. This is my code. It checks if the uid exists and then returns a boolean. which is parsed into an if statement in another method to add the contact. However, this method only returns false, even if the uid already exists in the contacts. Therefore allowing the adding of the same entry.
The method to check if contact exists.
public void addContacts(final String emailInput){
DatabaseReference users;
users = FirebaseDatabase.getInstance().getReference("users");
users.orderByChild("email").equalTo(emailInput).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
final DatabaseReference ref = FirebaseDatabase.getInstance().getReference("users");
for (DataSnapshot emailSnapshot : dataSnapshot.getChildren()) {
String emailData = emailSnapshot.child("email").getValue(String.class);
final String name = emailSnapshot.child("name").getValue(String.class);
String role = emailSnapshot.child("role").getValue(String.class);
if (emailData.equals(emailInput)){
key = emailSnapshot.getKey();
System.out.println(key);
if ((!role.equals(userRole))) {
DatabaseReference contactRef = ref.child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("contacts").child(key);
contactRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (!dataSnapshot.child(key).exists()) {
ContactProfile newContact = new ContactProfile(key, name);
ref.child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("contacts").push().setValue(newContact);
Toast.makeText(Contacts.this, "Contact Added Successfully", Toast.LENGTH_SHORT).show();
}
else {
Toast.makeText(Contacts.this, "Contact Already Exists", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
else if(key.equals(FirebaseAuth.getInstance().getCurrentUser().getUid())){
Toast.makeText(Contacts.this, "You cannot add yourself",Toast.LENGTH_SHORT).show();
}
else{
Toast.makeText(Contacts.this, "Cannot add user. \n They have the same role",
Toast.LENGTH_SHORT).show();
}
}
else {
Toast.makeText(Contacts.this, "Cannot add user. \n User does not exist",
Toast.LENGTH_SHORT).show();
}
}
}
Although i have no idea about firebase , but the problem might occur due to that you are using method inner class so the scope of value is out of this assignment ,and again you are comparing out of the method inner class so the value=1 will not happen it will always be value=0 so condition will never get true.
Try this...
DatabaseReference contactRef = userRef.child("Users").child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("contacts").child(key);
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()) {
value = 1;
}
}
Data is loaded from Firebase asynchronously. It is easiest to see what this means if you place a few log statements:
System.out.println("Before attaching listener");
contactRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
System.out.println("Got data");
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
System.out.println("After attaching listener");
When you run this code, it prints:
Before attaching listener
After attaching listener
Got data
This is probably not what you expected, but it explains exactly why your code doesn't work: by the time you check if (value.equals(1)){, the onDataChange method hasn't run yet.
As you see there is no way we can return the value from the database. Any return statement outside of onDataChange will run before the data has been loaded. And any return statement we run within onDataChange won't be able to return data to the calling function.
The quickest solution is to move all code that needs the data into the onDataChange method:
private boolean contactExists(final String key){
DatabaseReference userRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference contactRef = userRef.child("users").child(FirebaseAuth.getInstance().getCurrentUser().getUid()).child("contacts").child(key);
contactRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()){
... do what we need to do is the key exists
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
}
If you want to make the approach a bit more reusable, you can define your own callback interface and pass that into your contactExists. For an example of this, see my answer here: getContactsFromFirebase() method return an empty list
I use Firebase for multiplayer. Everything works great except that I need when one player closes app (onStop(), onDestroy() etc) the game ends. I have the following firebase structure:
"PVP" : {
"active" : {
"1536394217496" : {
"didGameEnd" : true,
"gameMaster" : {
"didPlayerAnswer" : false,
"name" : "stelios",
"playerAnswerCorrect" : false,
"score" : 2
},
"gameSlave" : {
"didPlayerAnswer" : false,
"name" : "anna",
"playerAnswerCorrect" : false,
"score" : 4
},
I have in onDestroy, onDestroy etc method to change the "didGameEnd" working fine. My problem is I need to catch the change to run the endgame code. I tried a lot but i cant manage it. Here is my code for this:
mGameFirebase = FirebaseDatabase.getInstance()
.getReferenceFromUrl(Constants.FIREBASE_BASE_URL + Constants.FIREBASE_PVP + Constants.FIREBASE_GAME_ACTIVE_PATH + mGameWaitModel.getId());
public void ifGameEnds() {
mGameFirebase.child("didGameEnd").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Boolean isEnded = (Boolean) dataSnapshot.getValue();
if (isEnded == true) {
Intent intent = new Intent(getApplicationContext(), PvPWinningActivity.class);
Bundle dataSend = new Bundle();
dataSend.putString("Winning Text", winning_text);
dataSend.putInt("Your Score", your_Score);
dataSend.putInt("Opponent Score", opponent_score);
intent.putExtras(dataSend);
startActivity(intent);
finish();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
As i can understand the valueListener catches the datachange so i dont need to do something special here. I guess the problem is with the boolean value. I tried it as string with no luck.
Set value works fine so i guess i m pointing in the right place
mGameFirebase.child("didGameEnd").setValue(true);
Appriciate any help for what is wrong as i m new at Firebase!
After many tries the following code
mGameFirebase.orderByChild("didGameEnd").equalTo(true).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
// this will be triggered only when the value is true, so you can add the thing that you want to make happen, here only
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
works BUT it triggers even of the value is FALSE. So actually its just read the value, not doing what i need.
The following code
mGameEnd =FirebaseDatabase.getInstance()
.getReferenceFromUrl(Constants.FIREBASE_BASE_URL + Constants.FIREBASE_TEST_PATH + Constants.FIREBASE_GAME_ACTIVE_PATH + mGameWaitModel.getId());
mGameEnd.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
boolean isEnded = dataSnapshot.getValue(Boolean.class);
if(isEnded) {
//Your logic
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
throws Nullpoint exception all the time.
To set the value for your didGameEnd property from false to true, you need to use the following lines of code in your onStop() method:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference didGameEndRef = rootRef
.child("PVP")
.child("active")
.child(uid)
.child("didGameEnd");
didGameEndRef.setValue(true);
To constantly check if this value is changed, you should add the listener on the same reference like this:
didGameEndRef.addValueEventListener(/* ... */);
And inside the onDataChange() method, get the value using these lines of code:
if(dataSnapshot.exists()) {
boolean isEnded = dataSnapshot.getValue(Boolean.class);
if(isEnded) {
//Your logic
}
}
I am guessing that your mGameFirebase looks something like this:
DatabaseReference mGameFirebase = FirebaseDatabase.getInstance().getReference().child("PVP");
If it does, you can use childEventListener to listen to the changes in didGameEnd child of your database. The code does not have many changes just, simple additions :
mGameFirebase.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
Boolean isEnded = (Boolean) dataSnapshot.child("didGameEnd").getValue();
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
If your mGameFirebase points to the reference of the active or children below the main node, you can try this, which will only be triggered when the game ends and the value changes:
mGameFirebase.orderByChild("didGameEnd").equalTo(true).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
// this will be triggered only when the value is true, so you can add the thing that you want to make happen, here only
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
For future reference the following code works:
// initialize the game state listener
mGameEnd = FirebaseDatabase.getInstance()
.getReferenceFromUrl(Constants.FIREBASE_BASE_URL + Constants.FIREBASE_TEST_PATH + Constants.FIREBASE_GAME_ACTIVE_PATH + mGameWaitModel.getId());
mGameEnd.child("didGameEnd").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()) {
boolean isEnded = (Boolean) dataSnapshot.getValue();
if(isEnded){
if (isGameMaster()) {
your_Score = 0;
opponent_score = mGameModel.getGameSlave().getScore();
winning_text = "You Lost!";
}
if (!isGameMaster()) {
your_Score = 0;
opponent_score = mGameModel.getGameMaster().getScore();
winning_text = "You lost";
}
Intent intent = new Intent(getApplicationContext(), PvPWinningActivity.class);
Bundle dataSend = new Bundle();
dataSend.putString("Winning Text", winning_text);
dataSend.putInt("Your Score", your_Score);
dataSend.putInt("Opponent Score", opponent_score);
intent.putExtras(dataSend);
startActivity(intent);
finish();
Toast.makeText(getApplicationContext(), "Game Over", Toast.LENGTH_LONG).show();
}
else {
Toast.makeText(getApplicationContext(), "Keep Playing", Toast.LENGTH_LONG).show();
}
}
Thanks a lot for the efford!
a2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//below
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
ref.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
#Override
public void onDataChange(com.google.firebase.database.DataSnapshot dataSnapshot) {
ra2 = dataSnapshot.child("a2").getValue(String.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
//fetching face value
ref.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
#Override
public void onDataChange(com.google.firebase.database.DataSnapshot dataSnapshot) {
rface = dataSnapshot.child("face").getValue(String.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
//fetching nothingdb which is equals to "zero" in Firebase by default.
ref.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
#Override
public void onDataChange(com.google.firebase.database.DataSnapshot dataSnapshot) {
nothing = dataSnapshot.child("nothingdb").getValue(String.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
//fetching addeddb which is equals to "one" in Firebase by default.
ref.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
#Override
public void onDataChange(com.google.firebase.database.DataSnapshot dataSnapshot) {
added = dataSnapshot.child("addeddb").getValue(String.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
//starting if condition
if (ra2==nothing){
if (rface==nothing){
a2.setBackgroundResource(R.drawable.rounded);{
Firebase refChild = ref2.child("a2");
refChild.setValue("rounda2");
refChild = ref2.child("face");
refChild.setValue("one");
}
else if (rface==added) {
a2.setBackgroundResource(R.drawable.crossed);
{
Firebase refChild = ref2.child("a2");
refChild.setValue("crossa2");
refChild = ref2.child("face");
refChild.setValue("zero");
}
}
}
});
}
});
I am trying to use the above written code so that once I click on the button the background image of the button is changed based on the data present in the Firebase but the If Condition is not working for reason.
It just ignores if (ra2==nothing){ and also the next if conditions.
The listener onDataChange() callbacks are asynchronous. ra2, nothing, added, and rface will not contain valid results when you compare them for equality because the onDataChange() methods will not yet have executed. This answer to a related question explains the execution order in more detail.
In addition, to compare Strings for equality you cannot use the == operator. You must use the equals() method or TextUtils.equals().
#qbix's answer is true, your if condition actually work but it does not receive the value of ra2, rface, nothing, and added yet.
Also, why should you create 4 different ValueEventListener while you listening to the same reference? You can use just 1 listener then put your if condition inside onDataChange, like this:
a2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
ra2 = dataSnapshot.child("a2").getValue(String.class);
rface = dataSnapshot.child("face").getValue(String.class);
nothing = dataSnapshot.child("nothingdb").getValue(String.class);
added = dataSnapshot.child("addeddb").getValue(String.class);
//starting if condition
if (ra2.equals(nothing)) {
if (rface.equals(nothing)) {
a2.setBackgroundResource(R.drawable.rounded);
Firebase refChild = ref2.child("a2");
refChild.setValue("rounda2");
refChild = ref2.child("face");
refChild.setValue("one");
} else if (rface.equals(added)) {
a2.setBackgroundResource(R.drawable.crossed);
Firebase refChild = ref2.child("a2");
refChild.setValue("crossa2");
refChild = ref2.child("face");
refChild.setValue("zero");
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG, "onCancelled", databaseError.toException());
}
});
}
});