I have a ListView/RecyclerView. Now, I want to add more information for each item and the information could be found in database according to the item value. How would you guy achieve it??
For the best practice, should I query database in the background thread? Should I cancel the query if user scrolling fast?
There is no "best practice" AFAIK, but the following is a short summary of your options:
Query on UI thread as the user scrolls
It is usually a very bad practice to execute queries on UI thread in general. It is a sin to do this in ListView.
That said, there are ORM libraries that perform lazy loading of nested objects on UI thread (e.g. GreenDAO). People use these libraries in applications that have ListViews and it even works alright for some of them.
I would strongly advice against this method.
Query of all items ahead of time
As #CommonsWare mentioned, for small to medium collections you can just load the entire dataset into memory on background thread and then bind it to the ListView. You will need to show some kind of progress indication while the data is being loaded.
The definition of "small" and "medium" is very vague, but I'd say that if you are sure that the dataset will not be larger than few MBs, then this method can work pretty well.
The drawback of this method is that the user will need to wait for the entire dataset to be loaded and bound to the ListView. Depending on the size of the dataset and the complexity of database scheme this might take a while.
Query a predefined number of items initially, and then perform additional queries as the user scrolls
This is the most complex scheme of all, but it is inevitable in some cases (e.g. "infinite list").
The idea is to get some predefined number of items into the ListView, and then track user's interactions in order to supply additional items.
For example, you can fetch 100 items initially, and then fetch additional 100 when the user scrolls to the end of the list.
An optimization of this scheme would be to fetch additional items before the user gets to the end of the list (let's say when he scrolled through 50 items). This allows to create a truly "infinite list" behavior.
Note that if your collection is very large, you will need to take care not only of adding a new items as the user scrolls, but also of removing "previous" items in order to avoid out of memory crash.
Query of additional information for items that are already shown
Sometimes you would like to perform some additional query after the item is already shown.
In this case, just perform the query in the background and bind the data to the item.
One caveat of this task is that if the view of the item is recycled (as is the case in RecyclerView by default), then when your background fetch is done the target View might show totally different item. Binding the returned data to this View would be a mistake.
One way of handling this is to cancel the fetch. However, this is cumbersome and error prone in practice.
The easier way is just set transientState flag when the query is initiated, and clear it when the query is completed.
Related
In the beginning we had listViews. As a developer, we had to recycle and reuse the views to have a fluid experience.
Then, came the recylclerviews. Now, all the recycling heavy lifting is managed by android library itself.
Using pagination, an infinite recycler view can be implemented, which loads the data when needed.
But there is one problem I am still facing in infinite recyclerview. How is the data in the adapter managed?
In most of the infinite scroll implementations of recyclerview, the new data is appended to the original data. This makes the size of data set ever increasing.
Why cant dataset itself behave like recyclerview and recycle its data, instead of appending? (Like a circular queue).
How can one manage the positions of itemviews, when the dataset is a circular queue. Is it unnecessary and yields too little performance improvement? Am I missing some design pattern?
It would be possible to clear data already loaded, but this way you have to load data not only for "bottom" views, but for "upper ones" too. So user wants to back to previous data and he needs to load that data again, that's the problem.
There could be more work about notifying data set changed in recyclerView.
You can implement it this way, but remember about pagination and how it is implemented - for example limiting data in SQL statements standard way can output different data each call, so user backing to previous data can see other results! Check Twitter pagination for one of the way to achieve same results each time.
Pros:
less data in memory
Cons:
more work to write the code
more data loading for users (more internet usage, more loading times - when users wants back to previous data)
pagination has to be designed more carefully
Now, I used ListView and ArrayAdapter to show data from sqlite. My old implementation is retrieve from db and set to arrayAdapter. Then set adapter in listview.
But now considering to move to efficient adapter. What if sqlite have thousands of records ? I knew I have to retrieve first 20 records then retrieve next items based on scroll. I wonder Can I get that implementation by using cursor adapter ? Or May I know better solution for that situation.
When having thousands of records in a Database/Server response or whatever you are fetching the information from, the best practice is to do use "Lazy Loading" technique, basically what it does is, showing chunks of data sets to the user for example, 50 elements at the time, so, there's no need to load 950 elements that might not even be used, this technique improves considerably the application response and your memory too, now how you implement it is really up to you, there's a BaseAdapter class to do it on your own, or a good implementation of Cursor/Array adapters might do the trick as well.
Is important to mention that cursors have one advantage:
Cursor lets Android manage resources more efficiently by retrieving
and releasing row and column values on demand.
Hope it helps!
Regards!
First of all, the main reason I use CursorAdapter is that the content will automatically change if the data in sqlite changes. I don't need to consider about refresh UI if something changes below.
Secondly, both the query of sqlite and listview already make some optimization you want. The sqlite will not give all the data on the moment of executing query by default. It uses a slide window to gradually give the query result. And listview also does not draw all the item at once, it draws the items as the user scrolls down.
Hope this helps, though doesn't exactly answer your question
you can involve a thread which retrieve data from database and put into a Map or List. then when you need data like per 20 records when you scroll read from Map. it may help you.
just make another layer between database and view.
Try to focus on general "dos and donts" regarding lists, e.g. is
adding and removing 30 items of a 200 item list better then clearing
and repopulating? Or any other tips in this area - I cant really try
on my phone, to fast for that :-)
Is there any way to calculate the memory overhead / calculation power of list operations. The background is as following:
I have a list view on a page, the page has e.g. 3 Tabs at the bottom (All, Search, Recent). Now if you click on a tab, the listview should show you the approriate items.
There are two different approaches now, one is:
Use a single ListAdapter, filter the items accordingly
- If you click All, just put all items from the DB into it
- If you click Recent, just put the items which meet the requirements
Use two (three..) ListAdapters, one for each category
- If you click All, setAdapter() of list to the approriate one
- If you click Recent, setAdapter() to appropriate one
We are talking about a list of 200 items, which are complex objects created out of a database. When e.g. searching for an item, you enter part of the title, and the list should only show the appropriate items. The items will not be recreated, I would query for the IDs only, and use the buffered items (see later on datastructure).
What I am also not sure about is "where to filter", I could do it in the database (select from where title LIKE abc) and then EITHER:
remove not matching items from the list and add all matching (but not included) items
clear the whole list, add all matching items
Again, to clarify the structure of the App data:
Database with raw simple entries (with IDs + title + ...)
HashSet with complex entries, created once from DB, readonly + always all entries
ArrayList of current entries shown in a listView
I hope you get my drift, I am trying to get a feel for "expensive" operations. Perhaps, as a last motivation to answer, I will write some cases down, and you can give an opinion about how costly they are:
Selecting N items (ID only) from DB with "title LIKE"
Iterating a list of 200 items with a "title.contains()" and using only matches
Removing 100 items from an arraylist SHOWN by an list view
Removing 100 items from an arraylist not shown, then connect and show
Thanks for any feedback, or any tips for bad practices. Especially possible event trigger problems by working on visible list elements, instead of doing it first "in the background" and then setting a new ListAdapter
I see you have accepted an answer already, but I think I don't agree, because ArrayList has to copy all elements, if in the middle is an element added or removed.
I understand you already have a HashSet with all entries.
In that case, I believe the most efficient adapter is a custom ListAdapter inspired from ArrayAdapter
your adapter stores an ArrayList mAllObjects of all entries (for the "all" tab).
your adapter stores an ArrayList mRecentObject of recent entries (for the "recent" tab)
your adapter stores an ArrayList mMatchObject of matching entries (for the "search" tab)
your adapter has two filters
the recent filter returns the mRecentObject list (and creates it if it does not exist already)
the match filter creates a new mMatchObject list and adds matching elements. There is no optimization to be done here. the delete()method on an ArrayList is O(n).
I'm not sure I understand what you're trying to do with the whole list thing, but regarding filtering, you should do it on the database side (in the select statement you mentioned)... especially since this application is for mobile (given the comment at the top) and you would want to offload intensive operations to server side rather than leave them on mobile.
Slight stab in the dark. From the (fairly cursory) "Designing for Performance" document, it would seem that object creation, especially short-lived ones, have very high cost. I would interpret this as coming from two places: 1) object creation overhead (especially for complex objects), and 2) invoking the GC when these go out of scope or are explicitly destroyed.
Thus, as a starting point, I'd argue that you would want to do the work in the DB, and push the deltas to the view. So, per your original question, do:
remove not matching items from the list and add all matching (but not included) items
I suppose you could write a synthetic benchmark for this to get a feel for the differences in speed. However, in my own code, I try and avoid short-lived objects as much as possible as suggested by the performance document. The impact of the GC is heavyweight, as it often will disturb the UI thread and make it hiccup as it goes about its work.
I have working activity with 12 spinners that are linked to a single database table of over 20,000 records. Each spinner is bound to a different query to make the selections dynamic (based upon the prior selections). The code works but I'm having awful performance due to the number of queries and size of the table in the database. The initial layout takes 20+ seconds to load. That is because the first spinner is set to an initial selection during the layout which causes the 11 other spinners to populate as well. Performance is also affected when using the spinners. If I go to change the first selection, it takes approximately 10 seconds for all the other spinners to update.
Where should I start in looking for better performance? The database table? The queries? Or should I avoid using 12 spinners?
It sounds like you're not stalling the UI thread, so that's good.
You can create indexes to speed up the database, but you'll probably get the biggest performance boost by splitting the queries up instead of doing them all at once. So the first Spinner would be the only enabled control when the Activity launches. Making a selection would fire the query for the second Spinner and enable that one, and so on.
There's a lot of useful performance stuff in this (1 hour) video:
http://www.google.com/events/io/2010/sessions/writing-zippy-android-apps.html
With that many options you should possibly consider going for a drill-down list. This will allow you to split up the queries (one per activity) and will also allow the current list to take up all of the screen. This way you can also provide a way for the user to filter the current list which is probably a must if you have a lot of options in it.
Also:
Set your layout first, then start loading your data. At least this way something appears while the user is waiting.
Do all long running work in an AsyncTask / Thread to avoid blocking the UI thread
Make sure you have primary indexes on your database tables and query using these where possible
Don't load all your data at once, just perform each query as needed
When I have a ListActivity and an Adapter how does Android handle a list with 200 elements.
Does it try to load all of them directly how does it wait till the user scrolls and then renders those elements?
Do I have to worry with performance when a list is too long?
Depends how are the adapters implemented.
If you have an adapter that is not subclassed (you use one that is provider by SDK) Android will try to load all of them directly.
I recommend to subclass SimpleCursorAdapter and implement your custom adapter. This way you will have for example 10 views (as many your screen needs), and the view it will be reused for the rest of the 190 records.
There are several parts to this question. First of all, the data itself. Is that coming from a SQLite database via a query? If so, you have a Cursor object, which contains the entire result. So if you have a query that yields 200 rows, you will have all 200 rows in memory (which is why it's so important to narrow your projection).
As for the list itself, that part is pretty efficient - Android will only create views for the elements that you can actually see. And, depending on what kind of views you have, and whether they support recycling, Android will actually recycle existing objects to minimize the amount of overhead for initialization and memory management.
I'm not sure how Android handles it internally. But most programs I've seen handle the issue by loading 20 or so items and then making the last item say "Load next 20 items". Then when you click it, it loads the next 20 items.