I am most concerned about Performance issue and don't want users to wait for progress.
I have a chatActivity, where i show a ListView.
Here i send a chatMessage
Chats chat = new Chats(chatBox.getText().toString(),Chats.TYPE_MINE, dt.format(now));
chat.personId = chatee.getMyId();
chat.isDelievered = Chats.DELIEVERED_NONE;
chats.add(chat);
mAdapter.notifyDataSetChanged();
Notice that Chat delievery is set to NONE Right now. So basically the message is being added to the Chat List even its not delivered yet.
Now on back thread here is what's happening
It takes few seconds to send message where i do this
boolean bool = sendMessage(m);
if (bool)
chatee.isDelievered = Chats.DELIEVERED_DONE; (MESSAGE SENT)
if (chatee.isDelievered == Chats.DELIEVERED_DONE)
{
app.mDbHelper.saveMessage(chatee); // SAVING TO DATABASE
Intent i = new Intent(Constants.REFRESH_NOTIF).putExtra("refresh",Constants.REFRESH_NOTIF);
context.sendBroadcast(i);
}
It will send a broadcast to the activity.
Now here is the problem.
Broadcast call this function
public void callUIMethodForRefresh(Intent intent)
{
String ref = intent.getStringExtra("refresh");
if (ref == null)
{
}
else if (ref.equals(Constants.REFRESH_NOTIF))
{
}
}
Here i am confused of how can I reset that previous Chat object added to my List.
Points to be noted , i can be sending messages at a very fast speed and the refresh could be called for an old message whereas a new message is already typed.
ONE way is i make a For loop and check for all the "ChatList" array for the message sent and then replace its delivery notice, but again this is very low performance incase i have 1000+ objects in the list.
Is there any way, i can attach the sqlite database with my listView adapter that automatically detects the changes and reset the listView etc and etc?
What could be the best strategies here to avoid performance issues.
I would suggest looking into ContentProviders and Loaders (specifically a CusorLoader). Combining these with a CursorAdapter, you can use the ContentProvider which inserts/deletes/updates your sqlite database and notifies your loader to reload it's dataset and update the CursorAdapter/ListView.
Related
As title what I want to do is only when database had change than run notification for it but a problem is when I run app the notification appear even the data is not change
here is my code:
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v= inflater.inflate(R.layout.main,container,false);
result= (TextView) v.findViewById(R.id.textView3);
notificationmanager = NotificationManagerCompat.from(ct.getcontext());
if (user != null) {
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
String post = dataSnapshot.getValue(String.class);
result.setText(post);
Notification notification = new NotificationCompat.Builder(ct.getcontext(), CHANNEL_1_ID)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setContentTitle(post)
.setContentText("test")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setCategory(NotificationCompat.CATEGORY_EVENT)
.setDefaults(Notification.DEFAULT_ALL)
.build();
notificationmanager.notify(2,notification);
}
}
#Override
public void onCancelled(DatabaseError error) {
}
});
}
else
{
Toast.makeText(getActivity(),"not yet login",Toast.LENGTH_LONG).show();
}
return v;
}
Save the value in your local preferences, then on next change, verify if the value if it is really changed by comparing with previously saved value.
You need to use addValueEventListener(), from the docs:
public ValueEventListener addValueEventListener (ValueEventListener listener)
Add a listener for changes in the data at this location. Each time time the data changes, your listener will be called with an immutable snapshot of the data.
Using addValueEventListener, everytime there is a change in the database it will be triggered.
From #mikasaloli's explanation, I suspect the problem lies more in in a mix of the way firebase RTDB( realtime database) / Firestore functions and the wrong listener.
The first problem is, firebase RTDB/firestore by default keeps a local cache copy of the references( not all of them) you've accessed on your phone. From their docs, they say, and I quote:
"Asynchronous listeners: Data stored in a Firebase Realtime Database is retrieved by attaching an asynchronous listener to a database reference. The listener is triggered once for the initial state of the data and again anytime the data changes. An event listener may receive several different types of events. This mode of data retrieval is supported in Java, Node.js and Python Admin SDKs."
What you need to do is bypass the local cache when reading data for the first time after app startup.
here's how I've been able to do that( with some slight tweaking)
The second problem is as previously mentioned by the others, addListenerForSingleValueEvent will get triggered only once. So, assuming you receive the notification change for the first time, your listener will automagically unregister itself implying you won't receive the subsequent onDataChange events.
Now, conjecturing from the previous paragraphs, the problem with the above code is:
it attaches a listener for single value event. which then gets triggered with the local ( cached ) version of the data which forcibly isn't the latest state of the server database ( if the application was offline while some changes took place online). But then, upon attaching this listener, it gets triggered with the local version of the data and unregisters itself afterwards. Implying, the above code will get triggered once at most and might never get triggered during it's lifetime while attached.
What I suggest, is you bypass the local cache and use a addValueEventListener.
How can I retrieve all data values from Firebase's real-time database when I load onStart event from my Android app without run any listener of Firebase?
Because I don't want to run any listener such as ("ondataChange, childAdded, childRemoved and etc ...") I don't need it.
From the Firebase documentation, the onDataChange method of the listener fires once when being attached and then every time there is a change. So, yes there is a way to fetch the data without an event. Though, this fetch would happen using listener only.
https://firebase.google.com/docs/database/android/read-and-write
Listen for value events To read data at a path and listen for changes,
use the addValueEventListener() oraddListenerForSingleValueEvent()
method to add a ValueEventListener to a DatabaseReference.
You can use the onDataChange() method to read a static snapshot of the
contents at a given path, as they existed at the time of the event.
This method is triggered once when the listener is attached and again
every time the data, including children, changes. The event callback
is passed a snapshot containing all data at that location, including
child data. If there is no data, the snapshot will return false when
you call exists() and null when you call getValue() on it.
There is no way to get the data from a Firebase real-time database or from Cloud Firestore without using a listener. Everything in Firebase is about listeners, even if you are getting data in real-time or if you are getting only once, you need to attach a listener on a particular location. Without it, there is no way you can get it.
Edit: From the official documentation regarding reading data in Android, here is a code snippet that demonstrates a social blogging application retrieving the details of a post from the database:
ValueEventListener postListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Post object and use the values to update the UI
Post post = dataSnapshot.getValue(Post.class);
// ...
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
};
mPostReference.addValueEventListener(postListener);
Though, it is late to reply here, but I am choosing to post here only rather than creating another thread on this.
IMO, the basic expectation from any database is to be able to read a selected set of data which is available in DB. It does not matter whether I have to attach a listener or write a custom code.
While I understand that the added benefit of real-time DB is that it is event driven and UI does not need to wait for user to initiate an action for getting the data, but how come there is no way to retrieve data if there is a need !! I have spent hours looking for this one way to fetch data. OOh :(
We developed a Java application which uses the TwinCat ADS library (DLL) to read, write and handle events from the Beckhoff PLC (CX5120).
We successfully run this on several machines but unfortunately we’re currently having a problem case where the event handling suddenly stops.
This is the exact scenario we went through:
Read, write and events are handled correctly.
Suddenly we don’t get any events at all anymore, reading and writing are still working correctly though.
Replaced the PLC for another one, started working successfully again. We assumed it was a licensing problem then.
After a week of unattended running, the same problem started again, PLC/ADS library seems not to be triggering events anymore and we can’t seem to get it working again in any way. Reading/writing still working as it should.
Tested using another PC with the Java application, same problem. So something in the PLC seems to freeze up / stop working.
Here's how we have setup the event handling:
// Implementation of the CallbackListenerAdsState interface
public class ADSEventController implements CallbackListenerAdsState {
......
// Register itself as listener for the ADS events (in constructor)
callObject = new AdsCallbackObject();
callObject.addListenerCallbackAdsState(this);
....
// Event handling
public void onEvent(AmsAddr addr, AdsNotificationHeader notification, long user) {
log.info("Got ADS event for handle[{}] and with raw data[{}]", user, notification.getData());
......
// Registering notification handles for PLC variables
// If we already assigned a notification, delete it first (while reconnecting)
JNILong notification = new JNILong();
if(var.getNotification() != null) {
notification = var.getNotification();
AdsCallDllFunction.adsSyncDelDeviceNotificationReq(addr,notification);
}
// Specify attributes of the notificationRequest
AdsNotificationAttrib attr = new AdsNotificationAttrib();
attr.setCbLength(var.getSize());
attr.setNTransMode(AdsConstants.ADSTRANS_SERVERONCHA);
attr.setDwChangeFilter(1000); // 0.01 sec
attr.setNMaxDelay(2000); // 0.02 sec
// Create notificationHandle
long err = AdsCallDllFunction.adsSyncAddDeviceNotificationReq(
addr,
AdsCallDllFunction.ADSIGRP_SYM_VALBYHND, // IndexGroup
var.getHandle(), // IndexOffset
attr, // The defined AdsNotificationAttrib object
var.getHandle(), // Choose arbitrary number
notification);
var.setNotification(notification);
if (err != 0) {
log.error("Error: Add notification: 0x{} for var[{}]", Long.toHexString(err), var.getId());
}
We managed to find the cause.
When we register a variable we get a handle (long) from the PLC, which, in our case unexpectedly started to be negative values after a while.
We also used this long value as user reference for notifications, however, we found the user reference is an unsigned long in the ADS library.
So if we set a negative value of e.g. -1258290964 as ‘arbitrary number’ in the adsSyncAddDeviceNotificationReq call, the CallbackListenerAdsState onEvent method’s parameter ‘user’ (Long) got the unsigned long representation of our signed long user reference, which is 3036676332.
In our Java application we used this user reference to match an event to a specific plc variable by this handle. Since, in our example, we expected -1258290964 but got 3036676332, we never handled any events.
I'm trying to write an Android service app which intercepts failed outgoing sms messages (due to service unavailable) to store them and try to resend them as soon as the phone service is back. I almost got it to work, but i have this problem, which is not so simple to explain but i'll try, hoping you understand:
I have set a ContentObserver on the URI content://sms even if what i'm interested on is content://sms/failure because if i set it on this last URI the onChange method doesn't get triggered, and i don't know why. Provided successfully sent and received messages don't bother me since, in the onChange method in the observer, I read just the content in content://sms/failed, here is the real problem: onChange gets triggered also on DELETE events, of course, which occur, for example, in the moment a previously failed message is succesfully sent. This is bad for my application because if I can't distinguish between a delete event and an add event i don't know if i have to add or not the first sms in the failed message queue into the "resend" list. So, my question is: is there a way to distinguish between delete an add events with a content observer?
PS: It would be nice to understand why a ContentObserver on content://sms/failed doesn't work.
PPS: I have another minor issue: i'm resending the messages using the SmsManager, which works fine, the only problem is i can only set the destination number and the body of the message but not the ID of the message, so when i resent an sms the system think it's just a new sms and not an old one being resent (and so the old failed messages remains in the queue and doesn't get removed by the system).
...is there a way to distinguish between delete an add events with a content observer?
Query the URI passed into the onChange() method. If the Cursor is empty - i.e., getCount() returns 0 - then it was a deletion. For example:
public void onChange(boolean selfChange, Uri uri)
{
boolean deletion = false;
Cursor cursor = getContentResolver().query(uri, null, null,
null, null);
if(cursor != null)
deletion = cursor.getCount() == 0;
else
return;
...
}
It would be nice to understand why a ContentObserver on content://sms/failed doesn't work.
That's just how the SMS ContentProvider is implemented. You can't get any more specific than content://sms.
I have another minor issue...
The ID for a message is assigned by the ContentProvider, and the soonest your app can know its value is in the onChange() method. You have no control over the assignment of IDs, so your app will have to track failed message IDs itself, and delete the appropriate message upon a successful send.
I have a chatting application that saves the messages in a Sqlite database. I need to load all messages for each chat when clicked by user and show them in ListView in an activity. So i was wondering how to do this ?
I thought of a couple of ways to do that
First option: get the data from database (messages i.e. Strings) before opening the messaging screen for the user in arrays and pass them through the intent when starting the messaging activity and then show the messages on onCreate() method.
Second Option: get the data (messages i.e. Strings) from database when activity is being created and show them.
Third option: Saving Persistent State according to Sqlite
Any other ideas are appreciated.
Thanks.
use 2nd option.just send the unique chat id to the chat screen and load all the messages of that chat from database in oncreate() method and then bind all messages to the listview.
In Activity, loading sqlite db in others thread, and when it load finish, print it in your list view.
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
//loading database
mHandler.sendMessage(msg);
}
}).start();
Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
//update listview
};
};
What I would do is to load them when you open the Activity, but I wouldn't load all messages but just the last X items. Most users won't scroll up to older messages 99% of the times.
You could for example load the last 10 messages and then (if the users make scroll up) load the other messages.
Luck!!