I'm using Firebase in android studio, and I want to make a signup and login project. I'm able to save data to the Firebase, but I can't retrieve data.
In every example, it's realtime --like, we set up a listener first and save data-- but mine is just storing and later retrieving.
Is there a way to just retrieve data? (I wish you could understand :( )
public void onLogIn(View v){
reference2 = fbData.getReference(loginId.getText().toString());
reference2.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String gotPw = dataSnapshot.getValue(String.class);
if(gotPw == loginPw.getText().toString()){
Log.d("TEST","DONE");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d("TEST","ERROR");
}
});
}
This code doesn't even call the onDataChange method.
In java == operator to compare string doesn't always return true even if the string values might be the same.
Try doing:
if(gotPw.equals(loginPw.getText().toString())){
Log.d("TEST","DONE");
}
Related
is there any ways to read data from Firebase once the Activity is loaded. At this moment I am using the regular valueEventListener, but in order for it to work, there has to be some sort of a change in the database
mDatabaseReference.child("Users").child(mUser.getUid()).
child("Posts").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
arrayOfQuestionForms.clear();
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
QuestionForm tempQuestionForm = postSnapshot.getValue(QuestionForm.class);
arrayOfQuestionForms.add(tempQuestionForm);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
What I am looking for is some way to read data from Firebase without using listeners. I have looked at other similar posts but I don't think there is a clear answer for this yet.
There is no way for reading data from a Firebase database without using listeners. Everything is about listeners when it comes to Firebase. It's true that when setting a value, we just need to use a method named setValue() directly on the reference. Unfortunately, there is no method within Firebase, let' say getValue(), which acts in the same way as setValue().
To solve this, i recommend you using addListenerForSingleValueEvent.
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.
in order for it to work, there has to be some sort of a change in the database
This is not true and a common source of confusion for developers.
With your current code, Firebase will immediately start reading the data from the server. Once it gets that data, it invokes your onDataChange().
From the documentation:
This method is triggered once when the listener is attached and again every time the data, including children, changes.
for such purpose I used different kind of listener - ChildEventListener. It has different #Override methods. The method onChildAdded returns every child-nodes of the node when called first time (i.e. on activity start).
Put attention - maybe you will need to slightly change the reference to DB (trim back one hierarchy level), to point to the parent node. If you expanded snapshot of your DB structure, I can look.
Here is updated code (sorry is made any typo - I couldn't test it as have no your DB :)
mDatabaseReference.child("Users").child(mUser.getUid()).child("Posts").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
arrayOfQuestionForms.clear();
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
QuestionForm tempQuestionForm = postSnapshot.getValue(QuestionForm.class);
arrayOfQuestionForms.add(tempQuestionForm);
}
}
#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) {
}
});
I have a firebase database architecture set up like the docs recommend (data fan out). I have a node with all "athletes" and another node with "teams." I'd like to retrieve the athlete's information from the "athletes" node based on the Athlete's ID inside the "teams" node.
My code looks like this:
if (eventListener == null) {
eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
mAdapter.clear();
athleteCount = 0;
final List<Athlete> athletes = new ArrayList<>();
final long childrenCount = dataSnapshot.getChildrenCount();
if (childrenCount == 0) {
hideProgressBar();
}
for (DataSnapshot ds : dataSnapshot.getChildren()) {
String key = ds.getKey();
athleteCount++;
if (!TextUtils.isEmpty(key)) {
mDatabaseReference.child(Athlete.ATHLETE).child(key)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Athlete athlete = dataSnapshot.getValue(Athlete.class);
if (athlete != null) {
athletes.add(athlete);
if (athleteCount == childrenCount) {
Collections.sort(athletes, new Athlete.AthleteComparator());
mAdapter.setRoster(athletes);
hideProgressBar();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mDatabaseReference.child("teams")
.child("-KnyqjH0acSol5cxHLM1")
.child("athletes")
.addValueEventListener(mValueEventListener);
Now this code does work fine, but I do think it's a little over the top to keep adding a listener, then populating my adapter after keeping track that the singleValueListeners have completed. There must be an easier way to do this. I'm asking because I'm also going to need to retrieve other data from another node and that's going be very messy.
If anyone knows a better way please let me know. Thank you.
Here's an image of my architecture:
I can think of a few approaches:
Extract the code that gets the athlete into a helper method, either with its own callback, or by passing the adapter (or view) along.
Preload all the athletes and then just do a direct lookup.
Duplicate the data that you immediately need about each athlete under the team node. Frequently this is just their name, so you'd have $athleteId: "Athlete's name".
There might be more options, but these are my typical go to's.
Oh btw, the key will never be empty, so this check (and indentation level) is not needed: if (!TextUtils.isEmpty(key)) {.
I have 2 branches in my JSON tree, which look like this:
users {
profile-data {
user-key {
//here is users profile fields like nickname and other
}
......
}
friends {
user-key {
friend's-user-key:true
another-friend's-user-key:true
......
}
}
}
The problem is to fetch all users-profile data of users, that are friends of any user. I didn't find the way to fetch all this data by 1 query so I need to write something like this:
usersFriendsRef.child("userId").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
String userId = child.getKey();
usersProfileDataRef.child(userId).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
////Only here i can get user's profile data
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
So the problem is that I need to make many requests to fetch friends' profile data one by one, so I need to restructure my database or find the way to write this as 1 query
That's the correct way in which you can achieve this. There's no problem with nested queries. You need to find all those id's and then based on them, to query. I could move the friends node under profile-data -> user-key to create a single query but knowing that in Firebase is best to have the data as flatten as possible, it's not a solution.
Keep in mind not to forget to remove the listener, when it's no more needed, like this:
databaseReference.removeEventListener(valueEventListener);
Hope it helps.
In my application I need to retrieve a value from one node in my database and use this value to pass in to another method elsewhere in my code.
Is it possible to read data from the data snapshot in firebase and return this value for use outside of the data snapshot?
For example, to return the phoneNum or groupId variables from the below method for use in another method:
public void getUserPhoneNum(String uId){
usersDatabase.child(uId).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists() || dataSnapshot.getValue() != null) {
User mUser = dataSnapshot.getValue(User.class);
String userPhone = mUser.getPhoneNum();
Group mGroup = dataSnapshot.child("groups").getValue(Group.class);
String groupId = mGroup.getGroupId();
groupsDatabase.child(groupId).child("members").child(userPhone).child("playing").setValue()
if (userPhone != null) {
}
}
else {
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
This is happening because onDataChange is called asynchronously. This means that the statement in which you get the phoneNum is executed before onDataChange has been called. If you want to use it outside, it will be null. So in order to use the phoneNum, you need to use it inside the onDataChange() method or for or another approach visit this post and this post.
Hope it helps.
It is not possible, because when you are using addListenerForSingleValueEvent you are defining a class that implements 'onDataChangeandonCancelled`.
You can instead use an interface to let the class now know when the phone number has been fetched.
I'm trying to implement firebase database in my app, but I'm having problems in retrieving data from multiple nodes at same time and updating the ui.
I'm storing all the Firebase methods in a separate class.
here is an example of one of the methods to retrieve one datum from a node.
public static void getTrainerCity(final Trainer trainer) {
dbref.child(USER_CITY).child(trainer.getId()).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
trainer.setCity(dataSnapshot.getValue(String.class));
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Then, to get for example one complete trainer, I do
public static void getTrainer(final boolean isAthlete, final String trainerId, Trainer trainer) {
trainer.setId(trainerId);
getTrainerCity(trainer);
getTrainerName(trainer);
getTrainerAge(trainer);
getTrainerNumber(trainer);
getTrainerEmail(trainer);
getTrainerGym(trainer);
getTrainerPhoto(trainer);
getTrainerStudyDegree(trainer);
getTrainerDescription(trainer);
if(!isAthlete) {
getTrainerAthletes(trainer);
}
}
when I call this method in UI, I'm not getting the entire Trainer object in one shot as those methods don't run on the main thread. This means that the trainer is constantly updated. However, I want to notify the UI only when all fields of the object related to the method have been set (I have to populate lists and initialize user). How would you do this?
I thought about attaching listeners, but it seems like I need a tremendous amount of them. Thank you guys
EDIT: This is how Db is structured
It's scenarios like this where RxJava shines.....what you could do is wrap firebase queries in Observable.create() and then use something like RxJava zip operator to combine all the queries. The alternative (to be avoided if possible) is to nest all the queries which leads to dreaded callback hell
For example you might have something like following for getTrainerCity
public static Observable<String> getTrainerCity(int trainerId) {
return Observable.create(subscriber -> {
dbref.child(USER_CITY).child(trainerId).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String city = dataSnapshot.getValue(String.class);
subscriber.onNext(city);
subscriber.onCompleted();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
});
}
You should get trainer node first and then parse the fetched data.
I assume you have a Trainer node which contains all the info (city, name, age, ...)
public static void getTrainer(final Trainer trainer) {
dbref.child(trainer_node).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Trainer trainer = dataSnapshot.getValue(Trainer.class);
String name = trainer.getName();
String city = trainer.getCity();
int age = trainer.getAge();
.
.
.
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
EDIT:
Data structure maybe like this
-users_node
--user1
---id:userid
---name: username
---city:usercity
---email:useremail
---workouts
----workoutid1:true
----workoutid2:true
--user2
---id:..
-athletes_node
--user1:true
--user2:true
--userX:true
-workouts_node
--workoutid1
---workout details here
--workoutid2
---workout details here