Android realm findAllAsync() freezing UI - android

when I'm querying lot of elements the UI get freeze.
realm.where(TVRealm.class).equalTo("favorite", true).findAllAsync()
.addChangeListener(new RealmChangeListener<RealmResults<TVRealm>>() {
#Override
public void onChange(RealmResults<TVRealm> element) {
List<TV> tvList = new ArrayList<>();
for (TVRealm tvRealm : element) {
tvList.add(prepareTV(TVRealm.toTV(tvRealm), true));
}
listener.onDataChange(tvList);
}
});
I thought that findAllAsync() will run on other thread and avoid the issue but not.
Does anyone knows how to avoid this issue? Maybe there is another way without using findAllAsync() method.
Thanks.

According to official Realm document, findAllAsync works in a background thread.
https://realm.io/docs/java/latest/#asynchronous-queries
I think your data changes too often and so you're trying to notify ui too often. So you're blocking ui. I guess you are notifying a RecyclerView adapter in your onDataChange method.
Also if your result list has too many items, every time when your data changed exploring the results and adding items to a new list may block ui.

If I don't make mistake, while RealmResults is updating, for each change you create new collection with new models and update UI. Try to call findAll() in another thread, map results to TVs and post completed list of TVs to UI thread.

why dont you try this
RealmResult<item> res=realm.where(item.class).where("name","sanjay").findAll();
it will get all the data at a time.

Related

Should I persist data objects in onSaveInstanceState

I'm using greenDAO in my android app to display a list of objects in a RecyclerView. I have a subclass of RecyclerView.Adapter that takes a list of objects which are greenDAO entities.
What I do in onCreate is:
Create an instance of my adapater passing null for my list. This is just to make the adapter known to the RecyclerView below.
Initialize the RecyclerView with layout and adapter.
Call a method that asynchronously queries the data using greenDAO and upon success updates the adapter with the actual list of objects so they are displayed.
This is the relevant code:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
mLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
mListAdapter = new MyRecyclerAdapter(null);
mList.setHasFixedSize(true);
mList.setLayoutManager(mLayoutManager);
mList.setAdapter(mListAdapter);
refreshItems();
}
public void refreshItems()
{
AsyncSession asyncSession = ((App)getApplication()).getDaoSession().startAsyncSession();
asyncSession.setListenerMainThread(new AsyncOperationListener()
{
#Override
public void onAsyncOperationCompleted(final AsyncOperation operation)
{
if (operation.isCompletedSucessfully())
mListAdapter.setItems((List<Item>) operation.getResult());
}
});
asyncSession.loadAll(Item.class);
}
This works pretty well. Now I noticed, that of course the method that queries the database via greenDAO is invoked every time I rotate the activity or come back to it from another activity. That's pretty clear, since I'm calling that method from onCreate.
My question is: is it best practice to do this like I'm doing it (requery DAO every time) or should I make my objects parcelable and save the list I have in onSaveInstanceState and restore it in onRestore instead of requerying DAO?
What you're doing is completely valid and you don't need to save the queried data in onSaveInstanceState(), use in-memory cache, or any other optimization (even if GreenDAO wouldn't have internal cache).
In fact, you're more than all-right because you perform the query asynchronously - GreenDAO's creators kind of claim that the queries can be executed on UI thread in most cases (which I find hard to agree with).
I would also suggest that you perform data query in onStart() instead of onCreate(). I personally think that onCreate() should be used only for operations you would otherwise perform in the constructor (e.g. fields initializations). Another reason to perform this query in onStart() is that if the user leaves your application for a long time and then gets back to it, the data might get outdated (e.g. due to background syncs by SyncAdapter) and you'll want to refresh it.
The last piece that you might want to add is "data change notifications". You will want this mechanism to be in place if the data that you query and display to the user can change without user's interaction (e.g. due to background syncs by SyncAdapter). The concept is simple - Activity registers for notifications about data change in onCreate(), and if notification received you perform re-query in order to make sure that the user sees an up-to-date data.
I can't claim that the above are "best practices", but they are good practices that work well.
Lazy list:
As #pskink suggested in his comment, you could also employ LazyList. Be aware, though, that it doesn't obviate a need for async query of data. Usage of LazyList allows you to perform the query as usual, but load the results into memory in on-demand way. This might be useful if you expect the query to produce lots of data.
In my opinion, however, one should optimize the code only if actual performance problem is being observed. So, unless you know ahead of time that a particular query produces thousands of results, I say you don't need LazyList.

Android: Output of thread on UI after it finishes its execution

Basically I have 2 tabs.
In which first tab contains the data, and the second one contains some summary kind of data based on first tab's data.
In the first tab there is list of events. Whenever the there is an add/update/delete operation on events the summary needs to recalculated. The recalculated summary creates the list of rows in the database. And from those rows the summary tab is displayed.
So the problem is summary calculation takes long amount of time. So it is blocking the UI thread. So I moved the calculations in the thread for smooth UI operations.
Now as summary calculation is running in thread, I don't know when the calculation is complete and now I need to update the summary tab data and showing something while calculation is going on.
The current setup which I have is something like this:
My DatabaseHelper Class:
add() {
...
// Do Add operation
...
reCalculateSummary();
}
remove() {
...
// Do remove operation
...
reCalculateSummary();
}
reCalculateSummary() {
new Thread(new Runnable() {
#Override
public void run() {
...
// calculate summary (may call another method)
...
// Add calculated summary to DB
}
}).start();
}
While the reCalculateSummary() is running, the summary to tab should show loading message.
So How can I achieve this whole situation working?
How can I know if thread has completed its execution? So that I can update summary tab data by retrieving new db values of summary data.
So here is some confusing situation. Please feel free to ask if you have any doubts in understanding the situation.
Thanks in advance!
Use an event bus, so you can update the UI, and avoid the limitation on AsyncTasks.
You can create the event "recalculateSummary", and just before job start (think of it as onPreExecute), update the UI with something that shows the user that the information on screen is not up-to-date, and update results when job is done.
Examples on event buses are Otto, EventBus, AutoBus ...
You can use an AsyncTask.
Do the processing in the doInBackground(..) method, and update the UI inside onPostExcecute(..) method

Rx java android chaining requests

As I am learning rx java, I have found another problem that I am very curious about.
I am trying to create load more with list and rx java android extension using Retrofit.
So I have created stream to my List and calling onNext whenever I need more data, this means (Its crucial) 2-3 at the very beggining, I am loading data at batches 10 per request, at first I do have to load them 3 times, one after another, then on every value I do apply a scan method to increase my value
.scan(0, new Func2<Integer, Integer, Integer>() {
#Override
public Integer call(Integer integer, Integer integer2) {
return integer + 1;
}
}
Then doing request using retrofit that returns list of Strings and changing adapter data
of course I am calling
.subscribeOn(Schedulers.newThread().observeOn(AndroidSchedulers.mainThread()
But I am getting an exception
java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes.
It seems that adapter data is trying to be modified from background thread, however I have checked, whenever I call adapter method to modify data I am on the main thread. This problem does not occur when I am loading single batch, only when trying to load few and well I cant figure out why its happening. Next side effect is that sometimes second batch is beign loaded before first. I have been messing around with blocking obvservable but without any result.
Thanks for any tips.
Edit
Pseudo code on my logic:
CreateListObservable
.scan(increase value by 1)
.flatMap(download batch of values)
.scan(add new batch to existing one)
.subscribe(Schedulers.newThread)
.observeOn(AndroidSchedulers.mainThread)
.subscribe(data -> adapter.changeData(data)
Edit 2
It seems than when I add some sleep between changing data (1s) this error dissappears, weird
Have you tried copying the output array before passing it to the adapter? If it is the array that scan retains, further requests may be editing the content of the array on the background thread due to the scan.
.subscribe(data -> adapter.changeData(data.copy())

notifyDataSetChanged() does not update the listview

I have a list of 4 items, I have used listview. I want to change a string dynamically on recieving internal event. I see that when I receive the event I am setting the string correctly but and then calling
mAdapter.notifyDataSetInvalidated();
mAdapter.notifyDataSetChanged();
but the list is not getting updated.
I've had the same experience. The cause was that the list adapter was updating on the wrong thread i.e. not the UI thread. This is easily solved by changing the adapter data on the UI thread through (as I found on other posts):
runOnUiThread(new Runnable() {
public void run() {
// code that changes the list adapter data
}
});
Of course you can always create a (inner) class that implements Runnable that is provided with the list adapter and data to add, insert etc.
Note: calling notifyDataSetInvalidated() or notifyDataSetChanged() will not be necessary as it is called by default, unless you turned it off explicitly with setNotifyOnChange(false);
I think that notifyDataSetChanged only works if you use the add (or insert), remove or clear functions on adapter.
You can rebuilt the list adapter for force to refresh the listView.
Excuse me for my bad english

Android: Modifying adapter content from a background thread

I've read up quite a bit on the exception thrown while using BaseAdapter, but i can't seem to find an alternative solution:
Make sure the content of your adapter is not modified from a background thread
What i want to achieve is to keep a copy of a message queue in memory, and use this message queue to populate the BaseAdapter for my ListView. The reason im doing this is that the message queue will keep getting messages from a socket even when the ListView is not currently present (for example a chat window).
The problem comes when i have the Activity with the ListView in foreground, BaseAdapter binded to the message queue's data, and a message comes in the socket. Adding the new message into the queue will throw the exception mentioned above. Unless i pre-populate my BaseAdapter with the message queue (as in the BaseAdapter having its own message queue) and updating both of them when a new message come in, i can't really find a way around this issue.
I don't really want to double up the effort on keeping those 2 queues up-to-date like this, surely there is a better way of doing this? Send broadcasts around doesn't work either because of the potential delay in the adapter serving a scroll and the notifyDataSetChanged call is made.
Use a Handler to modify the "message queue" from the main application thread.
I had this problem as well. It turns out I was recreating my adapter for new data but the old adapter was still set to the listview and would occasionally cause a crash. So I do something like this when I need to recreate the adapter:
//Clear adapter's array
if (!array.isEmpty()) { array.clear(); }
if (adapter != null) { adapter = null; }
setListAdapter(adapter);
//Later code
//array gets repopulated
//adapter gets recreated
setListAdapter(adapter);

Categories

Resources