Controling the occurrence of onChildAdded event in firebase database - android

I am working on android app in which I want to store unique IDs of users in firebase database, so to achieve this I have created a root, named "Users" and will store all the users unique IDs in this root.
So in order to check uniqueness I have to go through each child of "Users" and after checking the uniqueness I will add the uniqueID to "Users" root or will notify the user that uniqueId is not available.
When I check uniqueness, it's conditionals (if/else) execute before occurrence of OnChildAdded event, How to execute some code(mentioned in my code) after occurrence of OnChildAdded event
My Code
postBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(!uniqueIdText.getText().toString().trim().equals("")) {
uniqueness = true;
users.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if (dataSnapshot.getKey().equals(uniqueIdText.getText().toString().trim()))
uniqueness = false;
}
#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) {
}
});
}
else if(uniqueIdText.getText().toString().trim().equals("") )
Toast.makeText(LoginActivity.this,"Please enter id",Toast.LENGTH_LONG).show();
//======================How to execute it(code below) after occurrence of OnChildAdded event====================
if (uniqueness){
DatabaseReference usersInfo = users.child(uniqueIdText.getText().toString().trim());
startActivity(new Intent(LoginActivity.this, MainActivity.class));
}
else{
if(!uniqueIdText.getText().toString().trim().equals(""))
Toast.makeText(LoginActivity.this, "ID not available, Please enter a new one", Toast.LENGTH_SHORT).show();
}
}
});

There is no way to know when all the onChildAdded events have been called. But you can know when the first data from the database has been synchronized by using a ValueEventListener.
But in this case, I'd highly recommend taking a different approach. Downloading all UIDs to check if one value is present is incredibly inefficient and won't scale. Instead, attach a listener to the specific UID that you're looking for. If that value exists, you'll get the value. If it doesn't exist, you'll get an empty snapshot:
users.child(uniqueIdText.getText().toString()).addSingleValueEventListener(#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
uniqueness = false;
}
else {
uniqueness = true;
}
...
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "onCancelled", databaseError.toException());
// ...
}
};

Related

How to cut down repeated Firebase Listener

There are ten activity java code, seven activity need to firebase data. so i added firebaseListener(addChildEventListener). Because data is updated in each activity, each activity need to each Listener.
In my mind, it can work just one code...(because many people will say this is redundancy)
service, thred..? what i have to use.
mDatabase.child("Room-List").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
String key = dataSnapshot.getKey();
if(key.equals(roomName)) {
room = dataSnapshot.getValue(Room.class);
gap = room.gap;
nextTime = room.nextTime;
gameMode = room.gameMode;
break;
}
}
}
#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) { }
});
//Member-List - nickName, online, state, position
mDatabase.child("Room-List").child(roomName).child("Member-List").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
AllnickName = "";
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
Person person = dataSnapshot.getValue(Person.class);
if(person.online) {
online++;
AllnickName += person.nickName;
break;
}
}
TextView_person.setText(AllnickName);
TextView_roomName.setText(roomName + online);
}
#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) { }
});
}
There is no need for a Service or Tread to do this. When your app start you can spin up a Singletons in Android. In the Singleton you can host the ChildEventListener code and it can be accessed by any Activity who need the ChildEventListener Data.
If your Activity closes and restarted the Singleton will not be affected by that.
What is the purpose of Singleton?
The purpose of the Singleton class
is to control object creation, limiting the number of objects to only
one. The singleton allows only one entry point to create the new
instance of the class.
Since there is only one Singleton instance, any instance fields of a
Singleton will occur only once per class, just like static fields.
Singletons are often useful where you have to control the resources,
such as database connections or sockets.
If user or Android system closes your app the ChildEventListener and Singleton is also killed until user start your app again. If you need the ChildEventListener to be auto restarted in this case and so to speak "always be alive" you have to use a [Android Service][2]

Why onChildAdded is fired multiple times?

I have a problem with my code, I don't know if I get it because of onChildAdded or RecyclerView
Simply I want to display messages Inbox on RecyclerView that contains all messages I received from other users, each message contains the last message sent and information about senders such as his name and photo.
Like this
This is my structure
Messages
Receiver_UID
Sender_UID
push
message : "text"
I used 2 nested query to achieve that
so firstly I get sender UID, and then I get the last message from another query
dataQuery = FirebaseDatabase.getInstance().getReference("Messages").child(my_Id);
dataQuery.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot getalldata, String s) {
final String sender_UID = getalldata.getKey();
Query lastmess = FirebaseDatabase.getInstance().getReference("Messages").child(my_Id).child(sendUID);
lastmess.orderByKey().limitToLast(1).addChildEventListener(new ChildEventListener() { // last message
#Override
public void onChildAdded(DataSnapshot snap, String s) {
MessModel messages = snap.getValue(MessModel.class);
String Last_message = messages.getMessage();
getUserData(Last_message ,sender_UID);
}
#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) {
}
});
}
#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) {
}
});
And finally I get information of sender by this method
private void getUserData(final String last_message , String sendUID) {
FirebaseDatabase.getInstance().getReference("Users").orderByKey().equalTo(sendUID).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(final DataSnapshot data, final String s) {
listImessagesitem model = data.getValue(listImessagesitem.class);
name =model.getName();
uid = model.getUid();
//another information
result.add( new listImessagesitem( name ,last_message ,uid, ... another info));
useradapter = new listMessagesAdapter(getActivity(),result);
myList.setAdapter(useradapter);
useradapter.notifyDataSetChanged();
}
#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) {
}
});
}
It works well the first time and when I make refresh as well
The problem is when someone, in the list of messages Inbox, sends me a new message. In this case, if I didn't make refresh, I see two senders with the same information and different last messages ... If he sent again, I see 3 senders with same previous case and so on.
How can I fix it?
I mean How can I add the new message to the user if he already exists in my inbox list
Any help would be appreciated, Thanks
What I understand from looking at your code is that u have nested ChildEventListener's. So every-time the top listener is invoked a new inner child listener is created causing duplicate data as it calls the getUserData method to update the adapter.
What you can do?
1: Check if the result list contains the object if not then add otherwise ignore.
2: From what I understand, I believe you are trying to create a Mail Client application where two users are sending emails to each other. So instead of the structure that you have in your db why not create a single node for both the users something like this user_1_ID+user_2_ID .
This reduces the complexity as both the users will read from the same node and the message object should have a timestamp field to determine the latest message.

How can I get value of this child?

It's not current id that user logged... it's other uid from other user when other user send a request.
From what I understand, you want to get to know whenever you receive a friend request. I see that you've created a Friend_req node and have each user's keys inside it. This is good. I assume that you do have a minimal grasp of Firebase and flattening of data.
With this assumption, my answer is that you need a childEventListener on the node that you need to track for friend requests. The childEventListener has a onChildAdded() method that downloads data whenever a new child is added to the code ( in your case, a new friend request ). Here's a basic implementation.
FirebaseDatabase.getInstance().getReference().child("Friend_req")
.child(yourUserKey).addChildEventListener(new ChildEventListener() {
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
//Get notified on friend request
}
#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) {
}
});

Firebase: Retrieving data sorted by child value

My structure is like following
- Posts
- keu23jndf
- dateTime:"2376464"
- name:"abc"
- key34njf
- dateTime:"4333434"
- name:"xyz"
Now, I want to retrieve sorted data by dateTime in descending order.
Thanks
Ok so first of all I would suggest you to not use a client sided timestamp but instead use the ServerValue.TIMESTAMP. In most cases this is going to be the better setup. If you know what you are doing and you want it exactly like that, forget this and just read on.
The problem if you want to sort from newest to oldest is that Firebase does (as far as I know) not support this.
A workaround would be to use the negative value of the Firebase ServerValue.TIMESTAMP.
This is how I do it. It may not be the perfect or right way, but it does work
private void prepareUpload() {
//mDatabase is a reference to the root of the Firebase Database
final DatabaseReference timestampReference = mDatabase.child("timestamp");
final String timestampKey = timestampReference.push().getKey();
timestampReference.child(timestampKey).setValue(ServerValue.TIMESTAMP);
timestampReference.child(timestampKey).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null) {
Long timestamp = 0 - Long.parseLong(dataSnapshot.getValue().toString());
upload(/*Starting upload with new timestamp here (this is just a dummy method)*/);
timestampReference.child(timestampKey).removeValue();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG, databaseError.getMessage());
}
});
}
Now you have to set your Firebase Database rules as follows:
"Posts": {
".indexOn" : "date",
...
}
Finally you just have to query your 'Posts' somehow like this:
mDatabase.child("Posts").orderByChild("dateTime").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
//Do stuff with your data
}
#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) {
}
});
Hope this helps!

Knowing when Firebase has completed API call?

I am having some trouble knowing when my Firebase API call is finished. After reading the Firebase documentation I have found the following:
Value events are always triggered last and are guaranteed to contain updates from any other events which occurred before that snapshot was taken.
I understand this to mean that only after all the onChildAdded call is finished, then the ValueEventListener is called. As a result, I thought that I can populate my RecyclerView in the onChildAdded function and then the onSingleValueListener call, I can simply finish animating my loading screen (which has started animating before this function call) and proceed. However, I have run into an issue where I put some careful System.out.println statements and found that in my case, Test 1 is called before Test 2 is ever called. This causes problems because this is actually the opposite behavior of what I wanted: I wanted the onChildAdded function to finish and then call the onSingleValueListener function that prints out Test 1 to be called. Is there any reason why this is happening? Any way around this? I would appreciate an explanation on why this is happening. Thanks!
public void getComments(final String postId, final Activity activity, final View fragmentView, final View progressOverlay) {
final Firebase commentsRef = firebaseRef.child("/comments");
Firebase linkRef = firebaseRef.child("/posts/" + postId);
linkRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
System.out.println("Test 1");
if (progressOverlay.getVisibility() == View.VISIBLE) {
progressOverlay.setVisibility(View.GONE);
AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200);
fragmentView.findViewById(R.id.rv_view_comments).setVisibility(View.VISIBLE);
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
linkRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
commentsRef.child(dataSnapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Comment comment = dataSnapshot.getValue(Comment.class);
System.out.println("Test 2");
application.getCommentsRecyclerViewAdapter().getCommentsList().add(comment);
application.getCommentsRecyclerViewAdapter().notifyDataSetChanged();
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
#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(FirebaseError firebaseError) {
}
});
}
You may want to use the **FirebaseRecyclerAdapter** class that the Firebase team makes available in FirebaseUI-Android (see https://github.com/firebase/FirebaseUI-Android/blob/master/database/src/main/java/com/firebase/ui/database/FirebaseRecyclerAdapter.java)
In your gradle file add the line below (check here for latest version number in the readme)
compile 'com.firebaseui:firebase-ui-database:0.4.3'
With this code "Firebase linkRef = firebaseRef.child("/posts/" + postId);" I could see that you're using legacy Firebase API. Its deprecated now!
Kindly update your code to new Firebase 3.x.x API.
Below two are independent async call; Based on your use-case, you can use either one of the listener to read your data.
1. linkRef.addListenerForSingleValueEvent(new ValueEventListener() {});
2. linkRef.addChildEventListener(new ChildEventListener() {});
You can refer the firebase document to get more information about database listeners.
https://firebase.google.com/docs/database/android/retrieve-data
With the following code snippet, you can retrieve and populate your list of comments.
public void getComments(final String postId, final Activity activity, final View fragmentView, final View progressOverlay) {
DatabaseReference commentsRef = firebaseRef.child("/comments");
DatabaseReference linkRef = commentsRef.child("/posts/" + postId);
linkRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Iterate through data-snapshot, and update your Adapter dataset
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Comment comment = snapshot.getValue(Comment.class);
application.getCommentsRecyclerViewAdapter().getCommentsList().add(comment);
}
application.getCommentsRecyclerViewAdapter().notifyDataSetChanged();
// Dismiss your loading progressbar
if (progressOverlay.getVisibility() == View.VISIBLE) {
progressOverlay.setVisibility(View.GONE);
AndroidUtils.animateView(progressOverlay, View.GONE, 0, 200);
fragmentView.findViewById(R.id.rv_view_comments).setVisibility(View.VISIBLE);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Handle fail case here
}
});
Hope this would help you!

Categories

Resources