SoFunction
Updated on 2025-04-10

Android Development Notes: Deeply Understanding Cursor-related Performance Issues

When there is a large amount of data in the database, be careful when querying with Cursor, as it may cause performance problems. Cursors queried by database will be managed by a CursorWindow, including memory space application and data filling. CursorWindow has a limit on the content size in Cursor, which is 1024*1024, that is, 1M. In other words, the size of the data in Cursor cannot exceed 1M. If it exceeds 1M, the following error will be caused:

Copy the codeThe code is as follows:

08-23 05:48:31.838: DEBUG/Cursor(1805): skip_rows row 149
08-23 05:48:31.844: ERROR/CursorWindow(1805): need to grow: mSize = 1048576, size = 11499, freeSpace() = 7397, numRows = 80
08-23 05:48:31.844: ERROR/CursorWindow(1805): not growing since there are already 80 row(s), max size 1048576
08-23 05:48:31.844: ERROR/Cursor(1805): Failed allocating 11499 bytes for blob at 228,7
08-23 05:48:31.849: DEBUG/Cursor(1805): finish_program_and_get_row_count row 12
08-23 05:48:31.851: DEBUG/browser/BrowserBookmarkFoldersAdapter(1805): getView()-Position:149
08-23 05:48:32.019: DEBUG/Cursor(1805): skip_rows row 148

This shows that there is no free space in CursorWindow and is already applying for new space, but it seems that the application has failed. This error sometimes causes the query to get a Cursor null, and sometimes it does not cause too serious problems. However, it will cause performance problems, and the continuous application space will take up a lot of CPU time, which will cause the entire phone to become a jam. Especially the Cursor bound in ListView or GridView will cause it to be unable to slide, or the sliding becomes ten-point. Use the native Browser of Android 2.3 to open the history records. When there are more than 200 historical records, it will slide continuously, especially when sliding from bottom to upward, and for its bookmark, if the entry exceeds 100 and each has a thumbnail, the sliding will become a special card, and it cannot even be opened. This is the reason.
There is no fundamental solution to this problem. This is the limitation of the Android system. The only feasible thing is to find a way to avoid it, that is, to make the size of the Cursor less than 1M as much as possible. The following are feasible methods:
1. Query only the required fields
This is particularly important. It queries the required fields according to the needs of the UI display or actual needs. It is necessary to give the second parameter PROJECTION (uri, projection). If this parameter is null, all fields in the table will be queryed. Then, when the number of cursor size increases, it will grow very quickly. The reason for the history in Browser is that it queryes all fields during query, and several libraries store favicon and thumbnail binary files. Therefore, when these two fields are included, the capacity of Cursor can easily reach the limit.
2. Do not exist in the database
The database is only suitable for saving lightweight data that is easy to query and operate, such as shorter text, integers, booleans, floating point numbers, etc., and the purpose is to quickly search and query. For big data like pictures, longer text (such as articles), it is best to store them directly in the hard disk as a file, and then save their access paths in the database. For small images like favicon, you can also consider existing in the database, but for thumbnail images, it is not wise. Unless the entire application has a limit on the number (such as only a few dozen or a hundred), it is easy to reach the 1M limit when querying.
3. For particularly large amounts of data that exceed 5,000 or 100,000 or 100,000 or 100,000 or 100,000 or 100,000 or 100,000 or 100,000,000 or 100,000,000,000,000,000,000,000,000,000,
No matter how small the data volume of a record in the table is, when the number of pieces reaches level 5,000,000 or more, it will still reach the 1M limit. At this time, segmented queries are required, such as 500 or 1,000 queries each time. In addition, if you want to use it for display, so much data will be released at once, making it difficult for users to view it.
[Example] Android 2.3 bookmarks, history records and up to three pages to access. When the data volume reaches about 300, it will cause a very stuck sliding phenomenon. Especially when sliding from bottom to top, a special card will be sprinted:
Copy the codeThe code is as follows:

08-23 05:48:31.844: ERROR/CursorWindow(1805): need to grow: mSize = 1048576, size = 11499, freeSpace() = 7397, numRows = 80
08-23 05:48:31.844: ERROR/CursorWindow(1805): not growing since there are already 80 row(s), max size 1048576
08-23 05:48:31.844: ERROR/Cursor(1805): Failed allocating 11499 bytes for blob at 228,7

Such a LOG. And the bookmarks seem to be unable to open and slide, and their special cards.
The reason is that they all use the same field set when querying Browser.HISTORY_PROJECTION, which querys all fields in the bookmarks table. Bookmarks, history and most accesses are three different display pages, but their data is the same all from the bookmarks table. The Bookmarks table contains fields such as _id, title, url, bookmark, favicon, touch_icon, thumbnail, etc., where favicon and thumbnail are binary image data (byte[]). Browser.HISTORY_PROJECTION contains all fields, of course, also favicon and thumbnail. So when the entry reaches more than 200, Cursor will reach its 1M limit, which will lead to performance degradation and sliding to change the card.

In fact, thumbnail and touch_icon are not used at all for history and accessing up to two pages. It only requires _id, title, url, bookmark and favicon; for bookmark pages, thumbnail is only used in GRID. Therefore, you only need to remove the THUMBNAIL in the field set Browser.HISTORY_PROJECTION during query, which can solve the problem of sliding change.