How to get only newly added data from the Firebase? - android

As i am creating the chat app, for it i am using the Firebase.
It works perfectly, only one problem which i am getting that each time i am getting whole list of data from Firebase, when new item is added or deleted into it.
What i want that , only newly added record should come from Firebase , not whole list of data.
Please check my below code for it
ArrayList<ChatMessage> MY_ARRAYLIST= new ArrayList<>();
MsgViewHolder viewModel = ViewModelProviders.of(this).get(MsgViewHolder.class);
LiveData<DataSnapshot> liveData = viewModel.getDataSnapshotLiveData();
liveData.observe(this, dataSnapshot -> {
if (dataSnapshot != null) {
MY_ARRAYLIST.clear(); // I NEED TO CLEAR THE ARRAY-LIST TO GET THE REFRESHED DATA..I DO NOT WANT TO LOAD WHOLE LIST ON EACH TIME
if (dataSnapshot.exists())
for (DataSnapshot ds : dataSnapshot.getChildren()) {
ChatMessage bean = ds.getValue(ChatMessage.class);
assert bean != null;
MY_ARRAYLIST.add(bean);
}
}
});
As in above code i am using MY_ARRAYLIST.clear() to clear data, to get the newly added record.On each time from DataSnapshot , i am getting the whole list of data, whenever new record added or deleted.Is their any method to get only newly added data NOT whole List from Firebase?

You can use addChildEventListener() to you DatabaseReference which will notify you on different callback method when any child is add/removed/updated/deleted or moved
databaseRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
// If any child is added to the database reference
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
// If any child is updated/changed to the database reference
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
// If any child is removed to the database reference
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
// If any child is moved to the database reference
}
#Override
public void onCancelled(FirebaseError firebaseError) {
Log.e("FirebaseListAdapter", "Listen was cancelled, no more updates will occur");
}
});
The DataSnapshot in each callback will provide you the information of the child

This is by design, in a real-time system there is no concept of the "latest" data because it's always changing. However, if you want to only display items added to the list after the page has loaded, you can do the following:
var newItems = false;
var eventsList = new Firebase('https://*****-messages.firebaseio.com/');
eventsList.on('child_added', function(message) {
if (!newItems) return;
var message = message.val();
$.notification(message.message);
});
eventsList.once('value', function(messages) {
newItems = true;
});
above answer original by (anant)
(my) another solution
if you have control over your database schema you can add a 'datetime' element in your object and store the value of the time just before adding it to database in Epoch format, then you can simply get the list of objects in newly added order with limit like this.
ds.orderBy("datetimeSent", Direction.DESCENDING).limit(10);

I think you can use this listener onChildAdded() I provided a link to the documentation.
Listen for child events

You can try Query for query on database,add on child as date and time and query based on that data filed after every result update the query to last sync date and time

Related

How to prevent my chats from duplicating themselves whenever someone sends a message in Android Studio firebase

I am designing a simple basic chat app using firebase real time database and i've designed everything well, however, i'm facing one sllight issue. My chats keep duplicating themselves on the inbox page (the page whrere the chats are laid out for a user to select which chat he wants to open and start talking).
I've attached an image of what i mean below.
Screenshot of the phone screen
The code i am using to get the chats and display them in the recycler view is given below. I have a directory called Conversations in my DB that saves a user's Id and under it, theres a child of each and every person he chats wit, under which is the last message and a seen boolean.
Database Structure
The code is given below
convoref = FirebaseDatabase.getInstance().getReference().child("Conversations").child(currentUid);
and then..
public void getConvoIds() {
convoref.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
for(DataSnapshot convo : dataSnapshot.getChildren()){
boolean isMessageSeen = false;
String lastMessage = "";
if(convo.child("seen").getValue() != null) {
isMessageSeen = (boolean) convo.child("seen").getValue();
}else{
Log.i("nolastseen", "location is null");
}
if(convo.child("lastMessage").getValue() != null) {
lastMessage = convo.child("lastMessage").getValue().toString();
}else{
Log.i("nolastMessage", "location is null");
}
Log.i ("the_convo_partner_key", convo.getKey());
Log.i ("lastseenmessage", lastMessage);
Log.i ("seenstate", String.valueOf(isMessageSeen));
FetchConvoInfo(convo.getKey(), isMessageSeen, lastMessage );
}
}
}
the fetch convo information functuion is below
public void FetchConvoInfo(final String key, final boolean isMessageSeen, final String lastMessage){
FirebaseDatabase.getInstance().getReference().child("Users").child(key).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
boolean chatExists = false;
String username = "";
String thumbnail = "";
String chatPartner;
chatPartner = key;
if(dataSnapshot.child("username").exists()){
username = dataSnapshot.child("username").getValue().toString();
}
if(dataSnapshot.child("thumbnail").exists()){
thumbnail = dataSnapshot.child("thumbnail").getValue().toString();
}
ConvoClass obj= new ConvoClass(chatPartner, username, thumbnail, isMessageSeen, lastMessage);
resultConvos.add(obj);
mConvoAdapter.notifyDataSetChanged();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Any help would be greatly appreciated. i cant seem to figure out why the chat duplicates.
In your onDataChanged method, you are going through every child of the dataSnapshot. Each child of the data snapshot indicates a particular conversation of that particular currentUid guy. So when you are going through every child of the dataSnapshot you are adding all its children to the listview or recyclerview(I don't know what you are using. But you are adding it to the adapter). So you are adding the old data again and again whenever some new data must be added. Hence-duplicate data.
There are two common solutions.
The first is naive method. Do what you are doing right now. But while adding an item (chat, you will call it in your case, I think) to the adapter, check if it is already present in the container resultConvos. That will prevent you from adding duplicate chats. I am sure it is obvious to you also why this method is inefficient. You are unnecessarily having to go through every conversation of a person. It takes O(n) time for just adding one item.
The second method is the recommended method. Remove all the code of ValueEventListener. Instead use ChildEventListener. I don't know if you are aware of it. Check this.
ChildEventListener has mainly 4 methods instead of onDataChanged. Among that, what you require here is onChildAdded. Just like your onDataChanged, it has one argument- a data snapshot. But this data snapshot contains only the newly added child, whereas the data snapshot in onDataChanged contains the whole data of the conversations of that particular user (that means the whole list). So using the data snapshot provided by onChildAdded you can directly add only that chat to the adapter, which takes O(1) time.
For more about ChildEventListener, read that link I attached

Just Get Newly added Child Firebase android

i am trying to get reference or snapshot of newly added child on firebase
i have solve it by different way
myRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
}
myRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s)
{
}
My question is that both are return a complete snapshot means all this nodes in firebase is there any way getting child which newly added ("this will improve speed")
When you attach a listener to a location or a query, the listener will initially fire with all data that exists at that location or that matches the query. The database has no concept of "new" data, although your application may have that concept.
There are a few options to reduce the data that is retrieved, or the data that is passed to your listener:
If you're using disk persistence, the application may have a previous snapshot of the data in its disk cache. In that case, only the delta between the previous snapshot and the current snapshot is retrieved from the server. But the entire current snapshot is passed to your listener, so that you can render the UI correctly for the while current data.
If you only want to process the data since a certain moment, you'll need to only request that data from the database. You can typically do this by storing the key of the data you last processed in your local state, and then using a query like this to only get the new update:
myRef.orderByKey().startAt("mostRecentKeyWeHaveAlreadyProcessed").add...Listener

Unable to retrieve data from Firebase Realtime Database

I'm trying to display the "loc" of a part if its part number I given.
Here is what the data structure looks like:
{
"parts":{
"14521845": { "name":"TOOL EC160B/EC180B/EC210B/EC240", "loc":"EXC1", "sloc":"B3EGU01C03"},
"12829050": { "name":"SWITCH; IGNITION SWITCH", "loc":"PS01", "sloc":"85-06-013"},
"12829050": { "name":"SWITCH; IGNITION SWITCH", "loc":"COM1", "sloc":"B3RGK03D06"},
"20044893": { "name":"PARTS CATALOG_ENG_SPA_FRE_GER_KOR_EC210D", "loc":"EXC1", "sloc":"B3EGT01B02"}
}
}
Activity Code:
FirebaseDatabase firebaseDatabase=FirebaseDatabase.getInstance();
DatabaseReference databaseReference =firebaseDatabase.getReference("parts/"+curP);
databaseReference.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Products data=dataSnapshot.getValue(Products.class);
Log.i("",String.valueOf(data.getLoc()));
}
getLoc is the getter function for the Product class, and it returns the corresponding "loc" for the given curP. curP denoted the child values in parts.
The logic seems right to me, but I am not getting an output. Where am I going wrong here?
try this
getReference("parts").child(curP).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Products data = dataSnapshot.getValue(Products.class);
Log.i("", String.valueOf(data.getLoc()));
}
});
The problem is that what you are getting in onChildAdded() is not a whole Product object as you expect it to be.
In your database reference you are targeting a specific Product ("parts/"+curP) but using a ChildEventListener. The children of a specific product node are name, loc and sloc, so the onChildAdded() will trigger several times, giving you each of these properties as a dataSnapshot separately.
The two patterns you might use to get whole Product objects are either:
add a ChildEventListener directly to the "parts" node and you will get each of the Products as a child of that node, or;
if you are adding a listener directly to the node of a particular product, use a ValueEventListener, to get the whole of that nodes entry as one dataSnapshot.
You can try to use ValueEventListener. If you want read data once so use the addListenerForSingleValueEvent method, something like this:
private void getFirebaseLocValue(int curP) {
FirebaseDatabase firebase = FirebaseDatabase.getInstance();
DatabaseReference mDatabase = firebase.getReference("parts");
mDatabase.child(Integer.toString(curP))
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.hasChildren()) {
Products data = dataSnapshot.getValue(Products.class);
Log.e("TAG", data.getLoc());
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Or you can use addValueEventListener and will get data with any changes. I really don't think that ChildEventListener is a good idea to retrieve data from Firebase.

How to change value and re-trigger eventListener in Firebase?

I am making a public chat app using Firebase Real-time Database, but stuck on this problem. Like normal chat apps, I am displaying only few texts to the user and when/if the user scrolls the List is updated and more messages are displayed.
I have a listener to my Firebase reference, which is only displaying (say 10 messages):
int j = 10;
myRef.child("Chat").limitToLast(j).addChildEventListener(new com.google.firebase.database.ChildEventListener() {
#Override
public void onChildAdded(com.google.firebase.database.DataSnapshot dataSnapshot, String s) {
// Map<String,Object> datemsg = (Map<String,Object>)dataSnapshot.getValue();
Map<String,Object> msg = (Map<String,Object>)dataSnapshot.getValue();
String text = msg.get("Text").toString();
values.add(text);
#Override
public void onChildChanged(com.google.firebase.database.DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(com.google.firebase.database.DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(com.google.firebase.database.DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Now I have a scroll Listener that changes the value for j to j+10 but then the ChildEvenListener is not triggered and the new messages are not added to the listview.
I also tried using queries, but again having the same problem, after updating the value, how should I update the adapter?
No new child is added, thus the childevent listener is not triggered, but I want to change the size of
limitToLast(j)
and re-populate the list.
Any help would be appreciated.
Firebase queries are immutable. If you change the value you pass into limitToLast(), it becomes a new query.
So you must attach a new listener to the new query, and then repopulate the list with the items from that listener.
If you keep track of the key (dataSnapshot.getKey()) for each existing item in your list, you can optimize the updating of the list by checking if each item from the new listener is already present in the list.

Android - Persist data retrieved from Firebase

I have an activity and a model called CourseDetails.
String getData;
DatabaseReference mRef = FirebaseDatabase.getInstance().getReference().child("courses").child("Business");
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
CourseDetails c = dataSnapshot.getValue(CourseDetails.class);
getData = c.getCourseName();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
textview1.setText(getData);
Using above code throws NullPointerException at last line above. But if I put textview1.setText(getData) into the ValueEventListener, under getData = c.getCourseName(), the data can be displayed correctly.
Methods I found working are using SharedPreferences or setting data from a method such as public void display(String data) { textview1.setText(data); }. But what are the other ways to keep the retrieved data even if the data is outside ValueEventListener?
For instance I want to persist the data added into an ArrayList.
ArrayList<String> listData;
DatabaseReference mRef = FirebaseDatabase.getInstance().getReference().child("courses").child("Business");
mRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
CourseDetails c = dataSnapshot.getValue(CourseDetails.class);
String code = c.getCourseCode();
String name = c.getCourseName();
String CodeName = code + " " + name;
listData.add(CodeName);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
// data in ArrayList should be able to display here
StringBuilder builder = new StringBuilder();
for (String s : listData) {
builder.append(s + "\n");
}
textview1.setText(builder.toString());
How to achieve this kind of persistence?
As per my understanding, Firebase will notify all it's data listener attached to specific references (database references wherever the addValueEventListener is added) when those specific data gets modified. That is when
onDataChange will be called, when there is modification of the data at those database references,
(besides modification the method will always be called first time).
And this happens
asynchronously, so in the first case where null occurs because we don't know whether data is retreived from Firebase and
as far as I know, Android's main thread cannot be put on hold or pause until we retreive the data that's why we use Asynchronous tasks in Android.
So, I think the best way to do specific updates or task on data change is within onDataChange method. So, like you stated it could be
done by making those changes within onDataChange itself or by calling some other method from onDataChange.
Or, if you are using
adapter then, notifying adapter about the change within onDataChange. Also, you can take a look at other choice i.e. FirebaseRecyclerAdapter then,
it will handle the update automatically without any extra effort.

Categories

Resources