ListView.setListAdapter is very slow - android

I have a ListView which potentially contains thousands of rows, generated by a CursorAdapter. Only eight or so rows are visible at any one time. I've had reports that starting up this view can take many seconds, and can cause an ANR (force close).
I'm doing the DB query in a background thread. I've verified that newView and bindView in my adapter are only being called for the number of visible rows.
Once the list is displayed, actually scrolling up and down the list is very fast.
The delay is in the call to ListView.setListAdapter, which has to run on the UI thread. Why does this seem to depend on the total number of rows in the result set, rather than the (much smaller) number of rows which are actually being displayed? Is there any way I can optimize it?
This question was asked a couple of years ago in this thread. I'm hoping to get some fresh insight and more concrete examples of potential workarounds.
UPDATE
I have tried to work around this by using CommonsWare's EndlessAdapter. I limit the initial query to (say) 20 rows using a LIMIT clause in my DB query, and I increase this limit and resubmit the query every time I hit the bottom of the list.
(As an aside, I haven't found a way of appending just the new results to an existing Cursor, so I'm increasing the LIMIT value each time and then re-fetching the whole lot up to the new limit in a new Cursor.)
Strangely, this workaround doesn't seem to improve the time it takes to perform the initial setListAdapter call. When I run it on a data set containing only 20 rows, the call to setListAdapter is really quick. When I run it on a data set containing hundreds of rows, but limited to return just 20, it takes over a second.
UPDATE 2
By forcing the query to execute in the background thread with a simple getCount(), as suggested by CommonsWare, I've cured the initial blocking of the UI thread on starting the activity. The UI is still blocked, though, when returning to this activity from a child activity. The ListActivity by default seems to want to re-run the query on the UI thread.
I have worked around this by removing the adapter in onStop(), and recreating it in onStart(). Thus the query is always performed in the background regardless of the direction we're moving through the activity stack.

I'm doing the DB query in a background thread
If all you do in the background is call query() or rawQuery(), the query is not actually executed. It will be lazy-executed when you first try using the Cursor (e.g., getCount()). So, the right recipe for doing a query in the background is a rawQuery() followed by something like getCount() in the background thread, to ensure the query really is executed.
When I run it on a data set containing hundreds of rows, but limited to return just 20, it takes over a second.
Off the cuff, that would suggest that the speed issue is not the time required to read in the results, but rather in computing the results in the first place.

Related

Updated RealmResults with time-based query

I'm starting to approach this wonderful world of Realm. I'm very happy of the results I'm getting and now I have one question to submit.
In my android app I've got a Fragment that displays data retrieved from Realm. The query condition is that the time this data refers to is in between the beginning and the end of today.
RealmResults<Appointment> results = realm
.where(MyObject.class)
.between("begin", rangeBegin, rangeEnd)
.between("end", rangeBegin, rangeEnd)
.findAllSorted("begin", Sort.ASCENDING);
This query is executed in the onStart() method helping me to exploit the live-update feature, which indeed works very well.
I've also added listeners for changes in order to optimize UI updates.
Now the question is: how does this live-update behave if the time conditions change? (Imagine I keep the app opened for more than one day without touching it or simply I keep the app opened for minutes around midnight)
From what I've seen it seems to do the same query done the very first time onStart() was executed.
Is there a way to have also live-updating query or should I re-run that query somewhere else outside onStart()?
Thank you in advance
Now the question is: how does this live-update behave if the time conditions change? (Imagine I keep the app opened for more than one day without touching it or simply I keep the app opened for minutes around midnight)
The query is pretty constant after you've set it up, so you'd need to execute a new query with different parameters for rangeStart and rangeEnd, and replace your other results.
This query is executed in the onStart() method helping me to exploit the live-update feature, which indeed works very well. I've also added listeners for changes in order to optimize UI updates.
Personally I'd advise to put the query in onCreateView() instead, and the Realm lifecycle management to onCreateView() and onDestroyView().
Also, you can avoid manually assigning RealmChangeListeners for displaying lists if you use RealmRecyclerViewAdapter (adapters 1.3.0 works with realm 1.2.0).
If you use RealmRecyclerViewAdapter, then just call adapter.updateData(newResults); and it'll update the view as needed.

How to increase listview performance?

I have a scenario where i have to do following task:
1. populate a list-view.
2. perform database operation which is very time consuming task.
3. database processing/operation time is sometime unpredictable.
I have used listView using holder pattern, now it is working faster than earlier but still taking significant time. What else i can do to improve the performance.
I have an idea but i am afraid whether it will be good to implement or not. Idea is to put the database operation in AsyncTask and update my listview there only.
But i am afraid of doing so is because my listview is totally dependent on database result. So i can display something on Listview only when i am done with DB operation
Please suggest is using Async task will be good approach and please suggest any other idea.
Using the Holder approach is good. Use that in your adapters always. Make sure you are reusing the convert views as well.
Using an AsyncTask is the best option. But you don't have to wait for the entire operation to complete. Read up on how AsyncTask works. Use the publishProgress() method in your doInBackground() of the AsyncTask to give batches of data to your list.
For example, if you have to process 100 rows, process 10, then do a publishProgress() which will update the list with those 10 rows. When you process the next batch, update the list with publishProgress() again.

Downloading data in separate thread before initializing a ListView

I'm having issues with multithreading in my application. I know there are many posts on Threads/AsyncTasks/etc, but none seem to address my specific problem.
Basically, I get a query string in my search Activity, then send it to my results Activity, where the string is used as a SQL query, the results are returned as an array of JSON objects, then I display these objects in a ListView (which is part of the results Activity). All of my SQL connection and retrieval is done in a separate class that I call at the start of the results Activity.
MySQLRetrieve data = new MySQLRetrieve();
ArrayList<Tile> tiles = data.getResults(nameValuePairs, isLocationSearch);
The above code is how I get the SQL response and convert into an ArrayList, which I then use the populate my ListView with. getResults() takes care of all of this.
I already have separate threads working to download images into the ListView, but what I can't get to work is getting the SQL query and result to run in it's own Thread. What I want to achieve is this:
User enters search query in search Activity.
Intent is sent to results Activity, and it starts immediately.
ProgressDialog (just the animated spinner thing, not a loading bar) displays while the SQL query is taking place.
ListView populates with objects from the JSON array, lazy loading images as they come.
I have steps 1,2, and 4 working well, but 3 is the problem. I've looked up AsyncTasks, which seem to be the answer, but I just can't get them to work. Does anyone have a solution to this problem? I need to do this, so when starting the results Activity, the UI changes immediately to the results Activity and doesn't have to wait until the SQL response is returned.
And yes, I've already read the painless-threading post.
Thank you.
I would recommend against creating that ArrayList<Tile> to reduce memory consumption (and code size) and instead directly bind the SQLite Cursor to the ListView using a CursorAdapter.
That alone might just increase the performance enough that you don't need to do any async loading.
If you still want async loading, check out the LoaderManager framework (available since Android 3.0/ API level 11, with Android support package down to 1.6/4) which will automagically do asynchronous loading of your Cursor -- either using the built-in CursorLoader (if you happen to have a ContentProvider), or the SimpleCursorLoader created by a fellow SO user (if you don't).

android cursor movetofirst performance issue

i am trying to optimize my application. I noticed that cursor.movetofirst() method somehow slowing the performance of my code.
Cursor cursor = myDbHelper.getDayInfo(new SimpleDateFormat("yyyy-MM-dd").format(myCalendar.getTime());
above line executes in 10 ms in 2.1 emulator, and
if(cursor != null && cursor.moveToFirst())
this line took about 1.6 seconds. I made little search about this. Somepeople say make it in another thread or in asynctask, but this will make the code more complicated.
I 'm just trying to figure out what is actually happening to this cursor.
Can anyone simplify or give a hint about database performance increase related to my question?
It's been a while since this was marked answered but you never really got a great answer here. Even on a very large table a single row query should usually take much less than 1.6 seconds. The reason it's slow is that you don't have an index for the column(s) that you are querying on so it has to scan the entire table to find those values. If you create an index you can cut the time down to a fraction of a second.
It is natural, that moveToFirst() method gets much more time that other code. The data enquariyng actually takes place, which invokes database communication, data reading and so on. You actually can't do anything with it. The advice was right - move all the long operations to AsyncTask.
If the dataset held by the cursor is large, moving the cursor around takes time. You have to perform such data-intensive operations on a separate thread. Using an AsyncTask might make your code a tiny bit complex but it's worth it. This against the user thread being blocked at the cost of a broken UI experience.

Android queries modifying a variable in the UI Thread

I have a simple query returning a Cursor, and then I walk the cursor and create objects that I throw in an ArrayList, like this:
List<Element> myElements = new ArrayList<Element>();
Cursor c = db.query(...);
c.moveToFirst();
while (c != null && !c.isAfterLast()) {
myElements.add(new Element(cursor.getString(0).........)); <-- CREATING THE ELEMENT
c.moveToNext();
}
...
You get the idea.
The problem is that I need to run 4 queries like this hitting different tables, etc, but they all return the same Element object in the end (after walking the cursor).
Being a good Android citizen I created a class extending AsyncTask to avoid hogging the UI Thread. Also, I want to run the 4 queries in 4 threads to speed things up.
The question:
in my onPostExecute(Cursor c), I'm running the logic marked as "CREATING THE ELEMENT" above. If I run 4 threads with 4 queries and all modifying the List, will I have thread conflicts touching the same variable from them? How do I prevent that? Do I gain anything by threading this if the list I need to modify is synchronized? I mean, the threads will have to wait in line anyway, I might as well write the 4 queries and run them sequentially... or not?
I understand I want to keep this out of the UI Thread. The question is if I want to create 4 threads (each running in an AsyncTask) or just ONE AsyncTask that runs the 4 queries sequentially.
Thanks!
Llappall
will I have thread conflicts touching the same variable from them?
You will certainly have race conditions - if you are fine with it then no issues.
How do I prevent that? Do I gain anything by threading this if the list I need to modify is synchronized?
I don't think so.
I mean, the threads will have to wait in line anyway, I might as well write the 4 queries and run them sequentially... or not?
The question is if I want to create 4 threads (each running in an AsyncTask) or just ONE AsyncTask that runs the 4 queries sequentially.
I would run all the 4 queries in one AsyncTask, creating 4 AsyncTasks would be a lot to do and maintain.
Vector, as opposed to ArrayList, is synchronized and thread safe, so I would suggest to use it instead.
http://download.oracle.com/javase/6/docs/api/java/util/Vector.html
Another alternative would be to create a new List per thread and then use Collections.addAll() to incorporate the elements to the original list.
To answer the question whether you would gain anything by starting multiple threads, probably the answer will depend on how expensive are the queries you are doing. Starting a new thread has an intrinsic overhead, so you want to make sure that the query you are starting is worth the cost.

Categories

Resources