I have a chat application which is implemented using Firebase Realtime database entities only. No specific messaging components.
I'm adding a listener for the chat entity (or table) where the messages are stored. The listener is added by addChildEventListener which should call the onChildAdded method to bring all the children from chat entity in the moment it's attached and whenever a message is added.
The problem is that when I create the table, send the first message and attach the listener, I can see the table and message being created on the Firebase console, but the onChildAdded method is not called but a lot of time later. Then, as soon as it is called, sometimes more than 5 minutes later, I'm able to send and receive the messages as expected.
What could be causing this long delay?
I already tried to set the listener right after creating the table it listens to, but didn't work.
Creating the chat entity (table)
final DatabaseReference chat = dbReference.child("chat");
final DatabaseReference user = dbReference.child("user");
chatId = chat.push().getKey();
Log.d("mymessages", "createChat(), chatId = " + chatId);
HashMap newChatMap = new HashMap();
newChatMap.put("id", chatId);
newChatMap.put("users/" + currentUserId, true);
newChatMap.put("users/" + userId, true);
// Creating chats table
chat.child(chatId)
.child("info")
.updateChildren(newChatMap);
Log.d("mymessages", "Chat table created.");
Creating the message and inserting it in the chat entity
DatabaseReference newMessageDb = dbReference.child("chat").child(chatId).push();
Log.d("mymessages", "message created.");
Map newMessageMap = new HashMap<>();
newMessageMap.put("text", editTextMessage);
newMessageMap.put("creator", currentUserId);
newMessageDb.updateChildren(newMessageMap);
Log.d("mymessages", "message's text and creator added.");
Adding the listener
dbReference.child("chat").child(chatId).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
if (dataSnapshot.exists()) {
\\Fetch the messages...
} else {
Log.d("mymessages", "fetchChatMessages(), dataSnapshot does not exists.");
}
}
#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) {
}
});
Entire code that runs inside the button that sends the message
String editTextMessage = messageBodyEditText.getText().toString();
if (!editTextMessage.isEmpty()) {
if (!chatExists) {
final DatabaseReference chat = dbReference.child("chat");
final DatabaseReference user = dbReference.child("user");
chatId = chat.push().getKey();
HashMap newChatMap = new HashMap();
newChatMap.put("id", chatId);
newChatMap.put("users/" + currentUserId, true);
newChatMap.put("users/" + userId, true);
// Creating chats table
chat.child(chatId)
.child("info")
.updateChildren(newChatMap);
//Inserting the contact's id in the current user's chat table
HashMap newUserChatMap = new HashMap();
newUserChatMap.put(chatId + "/contact", userId);
user.child(currentUserId)
.child("chat")
.updateChildren(newUserChatMap);
//Inserting the current user's id in the contact's chat table
HashMap newContactChatMap = new HashMap();
newContactChatMap.put(chatId + "/contact", currentUserId);
user.child(userId)
.child("chat")
.updateChildren(newContactChatMap);
chatExists = true;
newChatCreated = true;
}
if (chatId != null) {
DatabaseReference newMessageDb = dbReference.child("chat").child(chatId).push();
Map newMessageMap = new HashMap<>();
newMessageMap.put("text", editTextMessage);
newMessageMap.put("creator", currentUserId);
newMessageDb.updateChildren(newMessageMap);
messageBodyEditText.setText("");
if (messageList.isEmpty()) {
if (chatExists) {
if (ConnectivityHelper.isConnectedToNetwork(this)) {
dbReference.child("chat").child(chatId).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
loadingMessages.setVisibility(View.VISIBLE);
loadingWarning.setVisibility(View.VISIBLE);
loadingWarning.setText("Loading messages...");
if (dataSnapshot.exists() && !dataSnapshot.getKey().equals("info")) {
loadingWarning.setText("Loading messages...");
String text = "";
String creatorId = "";
Object newText = dataSnapshot.child("text").getValue();
Object newCreatorId = dataSnapshot.child("creator").getValue();
if (newText != null) {
text = newText.toString();
}
if (newCreatorId != null) {
creatorId = newCreatorId.toString();
}
String creatorName = "";
if (!creatorId.equals(currentUserId))
creatorName = userName;
else
creatorName = creatorId;
Message message = new Message(dataSnapshot.getKey(), creatorName, text);
messageList.add(message);
messagesAdapter.notifyDataSetChanged();
recyclerView.smoothScrollToPosition(messagesAdapter.getItemCount() - 1);
loadingMessages.setVisibility(View.INVISIBLE);
loadingWarning.setVisibility(View.INVISIBLE);
sendButton.setVisibility(View.VISIBLE);
} else {
loadingMessages.setVisibility(View.INVISIBLE);
loadingWarning.setText("Messages not found. \nCould not load your messages :/");
}
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
if (dataSnapshot.exists())
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
loadingMessages.setVisibility(View.INVISIBLE);
loadingWarning.setText("Loading cancelled. \nCould not load your messages :/");
Log.d("mymessages", "fetchChatMessages(), onCancelled called.");
}
});
} else {
lyNoConnection.setVisibility(View.VISIBLE);
Log.d("mymessages", "Not connected to network.");
}
} else {
loadingMessages.setVisibility(View.INVISIBLE);
loadingWarning.setVisibility(View.INVISIBLE);
sendButton.setVisibility(View.VISIBLE);
}
}
} else {
Log.d("mymessages", "chatId = null");
}
}
Related
I am trying to display the messages received in the firebase database to the desired user.
The text, user1 sends is displayed properly on his device, but not in user2 device even though, the message reaches the firebase database. This is the main problem.
I am using the following code to check for new messages:
private void checkMessage(){
final DatabaseReference rRef = FirebaseDatabase.getInstance().getReference();
final DatabaseReference uRef = rRef.child("users");
final DatabaseReference mRef = uRef.child(toUid);
final DatabaseReference kRef = mRef.child("rec_msg");
kRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
if(mAuth.getCurrentUser().getUid().equals(toUid)) {
Map map = (Map) dataSnapshot.getValue();
assert map != null;
String message = map.get("rec_msg").toString();
addMessageBox(message, 2);
}
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
if(mAuth.getCurrentUser().getUid().equals(toUid)) {
Map map = (Map) dataSnapshot.getValue();
assert map != null;
String message = map.get("rec_msg").toString();
addMessageBox(message, 2);
}
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.i("Error", databaseError.getDetails());
}
});
The code that prints the box and shows the text on the screen is like:
private void addMessageBox(String message, int type){
TextView tv = new TextView(Main5Activity.this);
tv.setText(message);
tv.setPadding(20, 30, 30, 20);
tv.setTextSize(1, (float) 20.1);
LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lp2.weight = 1.0f;
if (type == 1) {
lp2.gravity = Gravity.END;
tv.setBackgroundResource(R.drawable.rounded_rectangle_grey);
} else {
lp2.gravity = Gravity.START;
tv.setBackgroundResource(R.drawable.rounded_rectangle_violet);
}
tv.setLayoutParams(lp2);
layout.addView(tv);
scrollView.fullScroll(ScrollView.FOCUS_DOWN);
}
The send button code works as following:
send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String meText = message.getText().toString();
if (!meText.equals("")) {
addMessageBox(meText, 1);
storeToUID(toUid, fromUid);
sendMessage(message.getText().toString(), toUid);
checkMessage();
}
message.setText("");
}
});
The database looks something like this:
The 'sendMessage()' and 'storeToUID()' just store the values on the firebase database.
Also, the 'checkMessage()' when not added inside the send button's listener code, crashes the app.
Where and how should I place the checkMessage() code so that the app works as it should?
My app allows you to add a Player to a node, once they have been added I am using the below method to search by phoneNum to see if that player exists in the 'users' node.
Where I am having trouble is that I need to be able to access the values from the datasnapshot for the user within the found matching node. e.g.return the value of the user id. When I attempt this it keeps showing as a null value and when I try to log to the console it complains that it is a null value which doesn't make sense as the if statement should take care of this.
The method is able to find if a player exists in the the users node and displays a Toast message to indicate this.
Code:
public void checkPlayerNum(final String phoneNum) {
DatabaseReference mDatabaseReference =
FirebaseDatabase.getInstance().getReference().child("users");
if (!TextUtils.isEmpty(phoneNum)) {
final Query phoneNumReference = mDatabaseReference.orderByChild("phoneNum").equalTo(phoneNum);
ValueEventListener phoneNumValueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null) {
// String key = String.valueOf(dataSnapshot.getValue());
User mUser = dataSnapshot.getValue(User.class);
String uId = mUser.getUid();
// Log.d("TAG",uId);
Toast.makeText(getApplicationContext(), "* found **"+ uId , Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(), "****NOT FOUND****", Toast.LENGTH_LONG).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
phoneNumReference.addListenerForSingleValueEvent(phoneNumValueEventListener);
} else {
Log.e("Error","phoneNum is null");
}
}
final Query query = mFirebaseDatabase.orderByChild("phoneNum").equalTo("userPhoneNumber");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
//here means the value exist
//do whatever you want to do
} else {
//here means the value not exist
//do whatever you want to do
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
Use this to check whether the data exist or not.
you need to do this at two steps , first check if the user exists. then you get user info by your userId.
DatabaseReference mDatabaseReference;
public void checkPlayerNum(final String phoneNum,final String userId) {
mDatabaseReference =
FirebaseDatabase.getInstance().getReference().child("users");
if (!TextUtils.isEmpty(phoneNum)) {
final Query phoneNumReference = mDatabaseReference.orderByChild("phoneNum").equalTo(phoneNum);
ValueEventListener phoneNumValueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.getValue() != null) {
// user exists
getUserInf(userId);
} else {
Toast.makeText(getApplicationContext(), "****NOT FOUND****", Toast.LENGTH_LONG).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
phoneNumReference.addListenerForSingleValueEvent(phoneNumValueEventListener);
} else {
Log.e("Error","phoneNum is null");
}
}
get user info by userId. mDatabaseReference is the same as before . define it as member field .
void getUserInf(String userId) {
userValueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// get user data
User mUser = dataSnapshot.getValue(User.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mDatabaseReference.child(userId).addListenerForSingleValueEvent(userValueEventListener);
}
your user Pojo should be like below.
public class User {
String email,name,phoneNum,uid;
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
public String getUid() {
return uid;
}
public void setUid(String uid) {
this.uid = uid;
}
}
Thanks everybody I managed to achieve it by using childEventListener
public void checkingNumber(final String phoneNum){
DatabaseReference mDatabaseReference =
FirebaseDatabase.getInstance().getReference().child("users");
final Query query = mDatabaseReference;
query.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if (dataSnapshot.getValue() != null) {
// String key = String.valueOf(dataSnapshot.getValue());
User mUser = dataSnapshot.getValue(User.class);
// String uId = mUser.getUid();
// usersDatabase.child(mUser.getUid());
// Log.d("TAG",uId);
if(mUser.getPhoneNum().equals(phoneNum)) {
String uId= mUser.getUid();
String email = mUser.getEmail();
String name = mUser.getName();
Toast.makeText(getApplicationContext(), "* found **" + " " + uId + " " + " " + email + " " + name, Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(getApplicationContext(), "****NOT FOUND****", Toast.LENGTH_LONG).show();
}
}
#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 function which write data into database
private void startCommenting() {
final String comment_val = meditComment.getText().toString().trim();
meditComment.setText("");
if (!TextUtils.isEmpty(comment_val)) {
mProgress.show();
final DatabaseReference newPost = mComment.child(post_key).push();
final String commentkey = newPost.getKey();
mUser.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Map<String,Object> checkoutData=new HashMap<>();
checkoutData.put("time",ServerValue.TIMESTAMP);
newPost.setValue(checkoutData);
newPost.child("comment").setValue(comment_val);
newPost.child("uid").setValue(dataSnapshot.child("id").getValue());
newPost.child("blogpost").setValue(dataSnapshot.child("blogkey").getValue());
newPost.child("userimage").setValue(dataSnapshot.child("image").getValue());
newPost.child("username").setValue(dataSnapshot.child("name").getValue());
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
After this function was called, a Query was made to get the data which contains the right post_key in the child ("blogpost").
mpostComment.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startCommenting();
mQueryCurrentComment = mComment.child(post_key).orderByChild("blogpost").equalTo(post_key);
mQueryCurrentComment.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String currentuserid;
String lastuserid = "";
String currentcommentuid;
for (DataSnapshot dsp : dataSnapshot.getChildren()) {
currentuserid = dsp.child("uid").getValue().toString();
Log.d(TAG, "user newid: " + currentuserid);
Log.d(TAG, "user oldid: " + lastuserid);
if (currentuserid.equals(lastuserid)) {
} else {
final DatabaseReference newCommentLike = mComment.child(currentuserid).push();
Map<String, Object> checkTime = new HashMap<>();
checkTime.put("time", ServerValue.TIMESTAMP);
newCommentLike.setValue(checkTime);
newCommentLike.child("location").setValue(location_key);
newCommentLike.child("category").setValue(category_key);
newCommentLike.child("pressed").setValue("false");
newCommentLike.child("message").setValue(" has also commented your post. ");
newCommentLike.child("blogpost").setValue(post_key);
newCommentLike.child(post_key).setValue(true);
}
lastuserid = currentuserid;
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
});
However, the Query was triggered twice, one before the new item was added, another after new item was added, which looks like below:
How can I only perform the actions inside Query after the newest item was added and not twice? Any help is appreciated!
All data.getValue() are returning null. I can get child key to return fine. I can write to firbase fine. Just data.getValue is returning null for some unknown reason. I have other classes working fine using the exact same format.
//method for retrieving info from firebase which has notifications
private void retrieveInfo(){
Firebase ref = new Firebase("https://fitnectapp.firebaseio.com/");
// Attach an listener to read the data at our posts reference
ref.addChildEventListener(new ChildEventListener() {
// Retrieve new posts as they are added to the database
#Override
public void onChildAdded(DataSnapshot snapshot, String previousChildKey) {
getUpdates(snapshot);
}
#Override
public void onChildRemoved(DataSnapshot snapshot) {
getUpdates(snapshot);
}
#Override
public void onChildChanged(DataSnapshot snapshot, String previousChildKey) {
getUpdates(snapshot);
}
#Override
public void onChildMoved(DataSnapshot snapshot, String previousChildKey) {
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
//All data values returning null??
private void getUpdates(DataSnapshot ds) {
if (ds.getKey().equals("notifications")) {
for (DataSnapshot data : ds.getChildren()) {
String user = sp.getString("username", null);
Notification notification = data.getValue(Notification.class);
if(notification.getNotifUser().equals(user)){
String sender = notification.getNotifSender();
String workout = notification.getNotifWorkout();
String notificationTxt = sender + " has liked your workout, " + workout;
notifications.add(notificationTxt);
}
}
}
}
Hi I was trying firebase with , My server has around 300 records which is getting synced with my app in 3 to four minutes in single, single way
cant it be like get record in set of 50 or something ??
If user is coming online after very long time then also I need this batching.
If user is installing app first time then also i need to download bulk record from firebase database
I am using
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.e(TAG, "addListenerForSingleValueEvent:onDataChange" + dataSnapshot.getValue());
reference.removeEventListener(valueEventListener);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG, "addListenerForSingleValueEvent:onCancelled" + databaseError.getMessage());
}
};
and
public class DBService extends Service {
private static final String TAG = DBService.class.getName();
private StartApplication application;
private ObjectMapper objectMapper;
#Override
public void onCreate() {
super.onCreate();
application = ((StartApplication) getApplication());
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference reference = database.getReference();
objectMapper = new ObjectMapper();
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
try {
String json = objectMapper.writeValueAsString(dataSnapshot.getValue());
Job job = objectMapper.readValue(json, Job.class);
ContentValues values = ContentValuesParser.parseJobs(job);
Uri uri = getContentResolver().insert(TableContract.JobsEntry.CONTENT_URI, values);
Log.d(TAG, "child add id " + job.getId() + " Uri " + uri.getEncodedPath());
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
//Uri uri = getContentResolver().insert(TableContract.JobsEntry.CONTENT_URI, values);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
try {
String json = objectMapper.writeValueAsString(dataSnapshot.getValue());
Job job = objectMapper.readValue(json, Job.class);
ContentValues values = ContentValuesParser.parseJobs(job);
Uri uri = getContentResolver().insert(TableContract.JobsEntry.CONTENT_URI, values);
Log.d(TAG, "child changed id " + job.getId() + " Uri " + uri.getEncodedPath());
} catch (Exception e) {
Log.e(TAG, e.getMessage(), e);
}
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
reference.child("jobs").addChildEventListener(childEventListener);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}}
Have you tried Querying data?
Query queryRef = ref.orderByChild("weight").limitToLast(50);
//This will fetch the last 50 children.
queryRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot snapshot, String previousChild) {
System.out.println(snapshot.getKey());
}