I have issues on nested queries I'm trying to do using firebase, seems like the first query (activity1) doesn't wait until the second (activity2) and third query (activity3) finished running, this might return NULL value from the first query. Please look at my sample for more understanding, I've been stuck here for days trying all kind of method but it just wont work. :(
Query query_1= reference.child("Users").child("Room")
.child("Profile");
query_1.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//ACTIVITY 1
Query query_2 = reference.child("Users").child(Room)
.child("Receiver").child("id");
query_2.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//ACTIVITY 2
Query query_3 = reference.child("chatrooms").child("Room")
.child("Creator").child("id");
query_3.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//ACTIVITY 3
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Related
I'm trying to get the count of "msanID" but it always returns the number as '0'. I'm using a separate class (ManageNodeName) to record the data and retrieving them back at list view.
I suspect the error is in the method of getting the String MSAN_NAMESP.
Any lead may help. Thanks!
List View
databaseNodes = FirebaseDatabase.getInstance().getReference("MSAN List");
#Override
protected void onStart() {
super.onStart();
databaseNodes.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
manageNodeList.clear();
for(DataSnapshot nodesnapshot: dataSnapshot.getChildren()){
progressDialog.dismiss();
ManageNodeName manageNodeName = nodesnapshot.getValue(ManageNodeName.class);
manageNodeList.add(manageNodeName);
MSAN_NAMESP = manageNodeName.getNodeID();
}
ManageNodeList adapter = new ManageNodeList(ManageSelectMSAN.this, manageNodeList);
manageListViewMSANs.setAdapter(adapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
query = FirebaseDatabase.getInstance().getReference("MSAN MTCE")
.orderByChild("msanID")
.equalTo(MSAN_NAMESP);
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
int size = (int) dataSnapshot.getChildrenCount();
ManageNodeName.setCount(size);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Data is loaded from Firebase (and most cloud APIs) asynchronously. While the data is being loaded, your main code continues to run, so that the user can continue to use the app. Then when the data is available, your onDataChange is called with that data.
This means that all code that needs the data, needs to either be inside onDataChange or be called from there.
So:
databaseNodes.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
manageNodeList.clear();
for(DataSnapshot nodesnapshot: dataSnapshot.getChildren()){
progressDialog.dismiss();
ManageNodeName manageNodeName = nodesnapshot.getValue(ManageNodeName.class);
manageNodeList.add(manageNodeName);
MSAN_NAMESP = manageNodeName.getNodeID();
query = FirebaseDatabase.getInstance().getReference("MSAN MTCE")
.orderByChild("msanID")
.equalTo(MSAN_NAMESP);
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
int size = (int) dataSnapshot.getChildrenCount();
ManageNodeName.setCount(size);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // never ignore errors
}
});
}
ManageNodeList adapter = new ManageNodeList(ManageSelectMSAN.this, manageNodeList);
manageListViewMSANs.setAdapter(adapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // never ignore errors
}
});
Also see: getContactsFromFirebase() method return an empty list for a longer example, and alternative approaches.
I am creating an android app using firebase. My firebase database structure is as follow-
Users:{
uid:{
name:Bruno,
age: 22,
email: bruno#gmail.com,
address:...
}
},
Level_info:{
uid:{
score:70,
level:5
}
}
Before Rxjava, I retrieve all users like this-
userRef.addValueEventListener(new ValueEventListerner(){
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(Datasnapshot snapshot:dataSnapshot.getChildren())
{
User user = snapshot.getValue(User.class);
userList.add(user);
//to get score and levels
/*I can't add nested value event listener directly because of Asynchronous behavior */
userId.add(user.getId());
}
getLevels(userId);
}
#Override
public void onCancelled(DatabaseError error) {
//handle errors
}
});
void getLevels(final ArrayList<String> userid){
levelRef.addValueEventListener(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(String id:userid)
{
Level level = dataSnapshot.child(id).getValue(Level.class);
levelList.add(level);
}
/** Attch both "userList" and "levelList" to RecyclerView */
}
#Override
public void onCancelled(DatabaseError error) {
//handle errors
}
});
}
It goes fine.But if there are many nested Listeners, It will hard to read.So I tried to change to RxJava and RxAndroid. Like this-
public Observable<ArrayList<User>> userOb(){
return Observable.create(new
ObservableOnSubscribe<ArrayList<User>>() {
#Override
public void subscribe(final
ObservableEmitter<ArrayList<User>> emitter)
throws Exception {
FirebaseDatabase.getInstance().getReference("Users")
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
ArrayList<UserModel> listUsers;
for(DataSnapshot snapshot: dataSnapshot.getChildren()){
User user = snapshot.getValue(User.class);
listUsers= new ArrayList<>();
listUsers.add(user);
emitter.onNext(listUsers);
}
emitter.onComplete();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
});
}
Here I don't know how to zip with LevelObservable because levelObservable need UserId from UserObservable to get data from Levels/(uid)/"level_info" .May be this is stupid question but I am new to RxJava. I want to get Corresponding userId from UserObservable to LevelObservable. So how can I do that?
Sorry for my bad English Writing skill.
Edit: Is there some ways to implement like this (because may be I miss some easy ways to implement).
I used recyclerview, arraylist, and firebase to get and show data, but data is added to the list too many times..
This is my code
database=FirebaseDatabase.getInstance();
mRef=database.getReference("SETS");
mRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot==null) return;
for(DataSnapshot userSanpshot : dataSnapshot.getChildren())
{
if(userSanpshot.child("DETAIL").getValue()==null) break;
single.Detail=userSanpshot.child("DETAIL").getValue().toString();
/~/
list.add(single);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
adapter.setItems(list);
and this is my code when I send data to firebase
Button sharebut=(Button)findViewById(R.id.usersend);
sharebut.setOnClickListener(
new FloatingActionButton.OnClickListener() {
#Override
public void onClick(View view) {
mDatabase.child("SETS").child(userId).child("DETAIL").setValue(detailText.getText().toString());
/~/
finish();
}
}
);
}
Try Using this method
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// do here your work
}
#Override
public void onCancelled(DatabaseError databaseError) {
// handle error here
}
});
The code looks like this:
final DatabaseReference TuidRef = usersRef.child(td);
final DatabaseReference msgRef = TuidRef.child("rec_msg");
final DatabaseReference FuidRef = TuidRef.child("fromUID");
final DatabaseReference secretRef = TuidRef.child("rec_secret");
msgRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot1) {
ms = dataSnapshot1.getValue(String.class);
flag++;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
FuidRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot2) {
fUid = dataSnapshot2.getValue(String.class);
flag++;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
secretRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot3) {
st = dataSnapshot3.getValue(String.class);
flag++;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
String flagS = "" + flag;
Log.i("flag",flagS);
if(flag > 2)
displayMessage();
I used the flag to know how many times the ValueEventListeners are triggered, but the flag is found to be 0, in the log.
td is the uid of the person who receives the message. In the picture below it is same as the parent key of other sibling Childs.
The database looks something like this, when there are no messages:
The database looks like this, when messages are received:
Edit: How can I execute the function displayMessage(), when all three of the listeners have been triggered at once?
The value is added simultaneously to all three of them in the firebase database, though.
You'll never know how many times a ValueEventListeners is triggered in the way do, because the value of flag will always remain 0. This is happening due the asynchronous behaviour of onDataChange() method which is called even before you are trying to get the data from the database. What's really happening is that you are trying to get the data from the database and immediately try to log it while the flag variable has the initial value of 0. Then, when getting the data from the database completes, it changes flag's value, but it's never read again.
A quick solve for this problem would be add three different flags inside each callback and try to log then separately only inside the onDataChange() method like this:
msgRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot1) {
ms = dataSnapshot1.getValue(String.class);
flag1++;
Log.i("flag1", String.valueOf(flag1));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});
FuidRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot2) {
fUid = dataSnapshot2.getValue(String.class);
flag2++;
Log.i("flag2", String.valueOf(flag2));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});
secretRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot3) {
st = dataSnapshot3.getValue(String.class);
flag3++;
Log.i("flag3", String.valueOf(flag3));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});
If you need in your code to get the value of that flag outside the onDataChange() method, I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.
Edit:
msgRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot1) {
ms = dataSnapshot1.getValue(String.class);
flag++;
FuidRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot2) {
fUid = dataSnapshot2.getValue(String.class);
flag++;
secretRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot3) {
st = dataSnapshot3.getValue(String.class);
flag++;
Log.i("flag", String.valueOf(flag));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});
I have very simple database
TokenAdmin:
---xccsaccxvkkgmcdds :
---token: "cxcxzcfsdfdsfdsfs"
---uid: "xccsaccxvkkgmcdds"
---hjhbnghtfgdgdgdffg :
---token: "vhghtytyryyr"
---uid: "hjhbnghtfgdgdgdffg"
Now in my code I want to check and execute some code only if it find uid equal to "hjhbnghtfgdgdgdffg"
Here is my code :
mDatabase.child("TokenAdmin").orderByChild("uid").equalTo(mycurrUser.getUID()).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//say here toast "A"
} else {
//toast "B"
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// ...
}
});
Now I run the code with mycurrUser.getUID() value set to "xxx" which I don't have listed in the database. I expected the code output was toast B, but instead I always get toast A, which in my understanding, it has found the value in the db. How to fix this, where did I do wrong ?
You need to use something like this:
mDatabase.child("TokenAdmin").orderByChild("uid").equalTo(mycurrUser.getUID()).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()) {
//Toast A
} else {
//Toast B
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
Hope it helps.
I'm confused why you're using a query to load the user. You seem to store the token under they UID key already, so this would work:
mDatabase.child("TokenAdmin").child(mycurrUser.getUID()).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
... user exists
} else {
... user node does not exist
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});