Retrieve list of Firebase database data with rxjava - android

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).

Related

Jump to Firebase parent Nodes from Child Nodes

Hello all i have a my fire-base structure like this
Firebase Database
I have an array list inside an node in fire-base , I need to loop through this list and jump to each ID to get information i need like the database photo shows.
what I've tried so far
public static void checkIfCurrentUserLiked(final HomeFragment.PostsViewHolder postsViewHolder, String postKey) {
userID = Objects.requireNonNull(FirebaseAuth.getInstance().getCurrentUser()).getUid();
likesRef = FirebaseDatabase.getInstance().getReference().child("likes");
postsRef = FirebaseDatabase.getInstance().getReference().child(postKey);
postsRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChild("likes")) {
for (DataSnapshot ds : dataSnapshot.child("likes").getChildren()) {
likesRef.child(Objects.requireNonNull(ds.getValue()).toString())
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
Like like = snapshot.getValue(Like.class);
if (like != null && like.getLikedUserID().equals(userID)) {
postsViewHolder.likesImage.setImageResource
(R.drawable.ic_item_home_post_like_filled);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) { }
});
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) { }
});
}
i have no error , just nothing happens , data are not fetched as expected
Change this:
postsRef = FirebaseDatabase.getInstance().getReference().child(postKey);
into this:
postsRef = FirebaseDatabase.getInstance().getReference().child("posts").child(postKey);
According to your database, you have node posts before the postKey, therefore you need to add it when referencing to a location.

How to perform nested query in firebase realtime database?

I fetched data from firebase database, inside OnDataChange method I want to perform another query based on the data I fetched previously. Before going to another activity I fetched all my data using a callback function MyCallback. Here listAllRequest contains expected data but userMap contains null. How can I ensure userMap contains data before going to another activity?
public void readRequestData(final Outgoing.MyCallback myCallback) {
groupDatabase = FirebaseDatabase.getInstance().getReference("Users").child(user.getUid()).child("outgoing");
userInfoRef= FirebaseDatabase.getInstance().getReference("UserInfo");
groupDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
listAllRequest.clear();
for(DataSnapshot childSnapshot: dataSnapshot.getChildren())
{
final BookRequestData bookRequestData = childSnapshot.getValue(BookRequestData.class);
listAllRequest.add(bookRequestData);
userInfoRef.child(bookRequestData.getOwnerId()).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
User user=dataSnapshot.getValue(User.class);
userMap.put(bookRequestData.getReqId(),user);
Log.e("outgoing",user.getName());
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
myCallback.onCallback(listAllRequest);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});
}

Is there any way to read data offline without setPersistance(true) with firebase

I want to read data offline for my users, but Persistance mode will make OnDataChange in ValueEventListener call twice. how can I prevent it. I need to use ValueEvent for my own purpose. someone help me please.
Update : I am making a friend suggestions list for users in my app, the suggested user must dont have any relationship with the current user. and this is my code ( I use Persistance for offline data as Firebase's suggestion)
valueEventListener_check_relationship=new ValueEventListener() {
#Override
public void onDataChange(#NonNull final DataSnapshot dataSnapshot_root) {
num_fiend_req=0;
num_sent_req=0;
update_badges(num_sent_req,tv_badges_sent_req);
update_badges(num_fiend_req,tv_badges_friend_req);
arrayList_suggestions.clear();
adapter_suggestions.notifyDataSetChanged();
GlobalConstants.ROOT_REF.child(GlobalConstants.CHILD_USERS)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot snapshot:dataSnapshot.getChildren()){
Object_User user_suggested=snapshot.getValue(Object_User.class);
if(!user_suggested.user_id.equals(GlobalConstants.getMyUID())){
if(!dataSnapshot_root.hasChild(user_suggested.user_id))
addToList(user_suggested);
else {
RemoveUserFromlist(user_suggested);
if (dataSnapshot_root.child(user_suggested.user_id)
.hasChild(GlobalConstants.CHILD_REQUESTING)){
String values=dataSnapshot_root.child(user_suggested.user_id)
.child(GlobalConstants.CHILD_REQUESTING)
.getValue().toString();
if (values.equalsIgnoreCase(GlobalConstants.VALUE_REQUESTING_SENDER))
num_sent_req++;
else num_fiend_req++;
}
update_badges(num_sent_req,tv_badges_sent_req);
update_badges(num_fiend_req,tv_badges_friend_req);
adapter_suggestions.notifyDataSetChanged();
}
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
};
GlobalConstants.ROOT_REF.child(GlobalConstants.CHILD_RELATIONSHIP)
.child(GlobalConstants.getMyUID()).keepSynced(true);
GlobalConstants.ROOT_REF.child(GlobalConstants.CHILD_RELATIONSHIP)
.child(GlobalConstants.getMyUID())
.addValueEventListener(valueEventListener_check_relationship);

Firebase - Adapter is called before my array has any elements and returns an empty RecyclerView

I'm facin a really weird problem, Im calling two nodes in my database , from one node I get all my user ids , then I just loop inside my user node to get the user metadata. The problem is that when I run the debugger, it seems like the mAdapter is beign called before the data is beign fetched. So my adapter set the recyclerview with no data. After the adapter has been called, my query fetching the user begins and the array is populated normally
This is what I have done so far
mDatabase.child("teamsNode").child(teamID).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot snapshot : dataSnapshot.getChildren()){
mDatabase.child("users").child(snapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
UserModel model = dataSnapshot.getValue(UserModel.class);
String playerName = model.getName();
model.setName(playerName);
mArrayPlayers.add(model);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) { }
});
}
mAdapter = new PlayersAdapter(mArrayPlayers,mContext,R.layout.recycler_row);
if(mAdapter.getItemCount()>0)
mRecyclerView.setAdapter(mAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) { }
});
As you can see, I set the adapter to the recyclerview after the first foreach that fetchs all the data, I know that beign asynchronous I need to wait for the inner mDatabase to be done but if I move the setting of the adapter one line above to wait for the second call to finish it does the same, any idea?
Database structure is like this
teamsNode
|__Jm2KSMjslpow2Ipoasz : true
|__601KSMjsldfjd2Ipos0 : true
|__asgm2Kshalpow2IposJ : true
users
|__Jm2KSMjslpow2Ipoasz
|_____name: randomname1
|__601KSMjsldfjd2Ipos0
|_____name: randomname2
|__asgm2Kshalpow2IposJ
|_____name: randomname3
Recyclerview is not populated. Any idea?
A weird thing is that sometimes runing it with the debugger does fill the data, but its rarely to happend.
If you want to only initialize the adapter when all data is loaded, you will need to do so in the nested onDataChange callback:
mDatabase.child("teamsNode").child(teamID).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshotTeam) {
AtomicInteger loadedUserCount = new AtomicInteger(0);
for(DataSnapshot snapshot : dataSnapshotTeam.getChildren()){
mDatabase.child("users").child(snapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
UserModel model = dataSnapshot.getValue(UserModel.class);
String playerName = model.getName();
model.setName(playerName);
mArrayPlayers.add(model);
if (loadedUserCount.getAndIncrement() + 1 == dataSnapshotTeam.getChildrenCount()) {
mAdapter = new PlayersAdapter(mArrayPlayers,mContext,R.layout.recycler_row);
if(mAdapter.getItemCount()>0) {
mRecyclerView.setAdapter(mAdapter);
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // don't ignore errors
}
});
The problem you have is that the calls to the addListenerForSingleValueEvent methods are asynchronous, so they are executed in another thread. When you call the second addListenerForSingleValueEvent this is executed in another thread, while the main thread follows its execution. You have to do the call of your adapter in the answer of your second call addListenerForSingleValueEvent in the onDataChange method. In this way to avoid anomalous operations of your application.
Hope this will help.
mDatabase.child("teamsNode").child(teamID).addListenerForSingleValueEvent(new ValueEventListener() {
mArrayPalyer.clear();//add this
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot snapshot : dataSnapshot.getChildren()){
mDatabase.child("users").child(snapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
UserModel model = dataSnapshot.getValue(UserModel.class);
String playerName = model.getName();
model.setName(playerName);
mArrayPlayers.add(model);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
mAdapter = new PlayersAdapter(mArrayPlayers,mContext,R.layout.recycler_row);
mRecyclerView.setAdapter(mAdapter); //makes changes here because you are checking for adapter item count before setting it and this always return null.
}
You can look at my code I am using ValueEventListener just like you and binding the data to recycler view
ValueEventListener postListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()){
UserListModel userListModel =dataSnapshot1.getValue(UserListModel.class);
userModelArrayList.add(userListModel);
}
userListAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(">>value", "loadPost:onCancelled", databaseError.toException());
}
};
//mDatabase is the reference to the Firebase Node
mDatabase.addValueEventListener(postListener);
userListRecyclerView.setLayoutManager(M.gridLayoutRecyclerView(getActivity() , 2));
userListAdapter = new UserListAdapter(getActivity(), userModelArrayList);
userListRecyclerView.setAdapter(userListAdapter);

Nested queries in firebase

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) {
}
});

Categories

Resources