Implementing an Efficient ContentProvider on Android - android

I'm currently working on a SQLite code base with a table that can hold a large amount of records. It has so many records that using a LoaderManager to asynchronously retrieve Cursor objects is becoming slow to display them in a ListView with a CursorAdapter.
If there is one row change in the table being queried, the LoaderManager is notified, and a new Cursor is retrieved. But, this seems inefficient because the Cursor queries for all the rows in the table for the ListView. The GUI isn't being blocked because the Cursor loading is being done in another thread, the problem is that the retrieval of the table rows can take a while. 5-10 seconds can pass on some slower phones before the new record information is displayed.
I'm trying to find a way to efficiently retrieve row changes to update the ListViews's rows without reloading everything.
I've looked into rewriting my code as a internal ContentProvider (hiding SQLiteDatabase) because I've seen it can be used with the app's ContentResolver to send out individual row change notifications via notifyChange().
If I switch to a ContentProvider, will it be as efficient as I've assumed? Upon individual record changes, can the ContentProvider send out events that will allow a ListView to reload only the row change information, and not require a complete requery of all the table information?

when implementing query() method of your ContentProvider return a custom. AbstractWindowedCursor, that way even if the final data set is huge you just fill the small window

Related

In Android SQLite, working directly with Cursor is more memory efficient than creating Model Objects?

In most of the Android sample codes, populating a ListView from SQLite database is done in two ways,
Prefetch data to List - Execute query, create Model objects for each row then add it to a List and close the Cursor, then populate ListView with List.
Without List and Model objects - Execute query and populate ListView by following the Cursor using moveToFirst, moveToLast, move, as required.
Now I want to know, which of the above method is more memory efficient, in Android ?
The Cursor approach is more memory efficient:
Suppose you have 1000 entries in your database and you have a ListView which can show 10 entries at the same time. If you create a list at first, you'll have to create 1000 model objects (each of which in turn consists of several objects depending on the number of columns of your table) and the listview creates additional 10 views (actually some more, depending on the layout of the list) for displaying the 10 items. Now when the user scrolls the list, in your Adapter you end up copying data from your model objects to the list item views currently in view.
On the other hand, if you use a CursorAdapter, whenever you have to fill a list item with data, you are provided with the Cursor holding exactly the data for that row and you can simply pick the data of the columns you actually need to be displayed in the list item. No need for creating the 1000 model objects.
From a code readability perspective, a model approach would be better because working with Cursors is quite low level, you'll need to know the names of the columns in the database and so on.
I think you need to use Service or at least Thread/Async so your UI thread will not be blocked. Service is better because people can go to other apps while downloading. You can use BroadcastReceiver to communicate with your running Service.

Better to use Cursor adapter or Array adapter

i have around 100's of schedules stored in database which needs to display them based on Listview based on requirements like, weekly basis, next week, next month, over due schedules etc...
Is it good to load all the schedules on launch of application and show them based on the option user chooses (Weekly, overdue, monthly etc...) in array adapter. Or at run time use the query, fetch the results from DB and use the cusor to load the data on listview using cusoradapter?.
Which method is effeciant?, i feel querying the DB always is expesive operation? is it really true?.
In your case CursorAdapter is more appropriate when there is a database because it does not load all the records as ArrayAdapter. It loads only the visible records, or the records you are querying. Here is the documentation for CursorAdapter:
Adapter that exposes data from a Cursor to a ListView widget.
The Cursor must include a column named "_id" or this class will not work. Additionally, using MergeCursor with this class will not work if the merged Cursors have overlapping values in their "_id" columns.
As from the doc of Content provider so it might not useful for you.
You don't need a provider to use an SQLite database if the use is entirely within your own application.
You can choose CursorAdapter over ArrayAdapter.
Better use CursorLoader
A CursorLoader runs an asynchronous query in the background against a ContentProvider, and returns the results to the Activity or FragmentActivity from which it was called. This allows the Activity or FragmentActivity to continue to interact with the user while the query is ongoing.
Loader Introduced in Android 3.0, loaders make it easy to asynchronously load data in an activity or fragment.

Android SQLite get all data from cursor fast

I am dealing with a performance problem in one of my apps. Maybe one of you guys can help me out?!
I have a database with somewhat around 10k entries.
I query for elements using the default query method from the SQLiteDatabase-class.
The query itself is fast enough.
Once the query is complete I have to display the results on a Google Map.
For that I generate a result array from the cursor which holds the marker information.
The method I use looks somewhat like this:
final ArrayList< MarkerElement > result = new ArrayList< MarkerElement >();
cursor.moveToFirst();
while ( !cursor.isAfterLast() ) {
result.add( new MarkerElement(
cursor.getString( COL_TITLE ),
cursor.getString( COL_SNIPPET ),
new LatLng(
cursor.getDouble( COL_LAT ),
cursor.getDouble( COL_LNG ) ),
cursor.getString( COL_OTHER_USEFUL_DATA ) );
cursor.moveToNext();
}
cursor.close();
Where MarkerElement is simply a class that holds the required values for a Marker on a Google Map.
The problem now is, that looping through all cursor elements takes by far to long.
Also I cannot think of a smart way to lazy load the results like in a ListView because I need to show all results simultaneously.
Is there anything I can do to speed up this process significantly?
Any help is greatly appreciated!
Best regards
I'm not so sure the query is really as fast as you think it is. Most likely the actual query is only executed with the cursor.moveToFirst() statement and not when calling query() or rawQuery() (or whatever other query method you're using).
Anyway the query should be fast enough to keep the user waiting only shortly. If not then you might want to consider loading it in chunks using SELECT * FROM your_table LIMIT START, COUNT (e.g. SELECT * FROM your_table LIMIT 0, 1000 to retrieve the first 1000 rows).
The query can't happen on the ui thread so you want to run it in an AsyncTaskLoader or better even a CursorLoader. A CursorLoader without ContentProvider is possible as you can see here: https://stackoverflow.com/a/7422343/534471.
Let's assume you need to run 10 queries for 1000 records each, then you would have 10 CursorLoaders that you could manage using a LoaderManager. LoaderManager manages the cursors (opens and closes them), retains the cursors across orientation changes and runs everything in a background task so no issues with blocking the ui thread. LoaderManager also re-queries the db should the content change (see: https://stackoverflow.com/a/5603959/534471).
When the LoaderManager notifies your fragment or your activity that a cursor has finished loading it will call onLoadFinished() (see: http://developer.android.com/reference/android/support/v4/app/LoaderManager.LoaderCallbacks.html).
What slows down your code isn't just querying the database but also creating 10'000 MarkerElement and another 10'000 LatLng objects. I don't know your requirements but if you see any chance to work without these objects that would certainly speed up your code. Another desirable effect of eliminating the MarkerElements/LatLng is memory usage. For an app targeting phones 20'000 objects with 3 Strings and 2 Double is considerable.
Using CursorLoaders and LoaderManager would allow you to retrieve the values from the cursor and to populate your ui views directly without MarkerElements and LatLng. It would also allow you to load lazily. You can populate the views whenever onLoadFinished() is called for one of the CursorLoaders (onLoadFinished() is called on the ui thread unless the initLoader/restartLoader is called from a non-ui thread). If 1000 views is too much to populate at once, either break down the query into smaller pieces or add a mechanism to populate the views in chunks of 10s or 100s (for each cursor).
In case you need the information you currently store in MarkerElement e.g. when the user selects one of the markers, use setTag() on the view that displays the marker to store the primary key of the db record. Use the key to either retrieve the record from the db or better even from the already queried cursors (that will need some mapping mechanism but it's doable).
Summary:
Split the query into several sub queries to retrieve smaller sets of data
Use CursorLoaders and CursorManager to manage the different queries/cursors
Don't create a MarkerElement and a LatLng for each row but populate the views directly from the returned cursors
Possibly do the populate for each cursor in several steps to keep the ui responsive
use the setTag() on the views to be able to retrieve the data behind the view

Listview Adapter with SQLite Query's

I have a quick performance question; I have a ListView adapter which populates the listview and I open my sqlite database and close it at the end of populating the view and I populate some information about the list row from the SQLite database. I have noticed a serious performance hit with doing this mainly when scrolling which makes sense. I'm wondering how I can improve the performance of scrolling. I query the database about 3 times.
Database db = new Database(context);
db.open();
viewHolder.first.setText(db.queryFirst());
viewHolder.second.setText(db.querySecond());
viewHolder.third.setText(db.queryThird());
db.close();
Should I keep a reference to DB as an instance variable and just open and close when I query or how should I go about this?
I'm wondering how I can improve the performance of scrolling. I query the database about 3 times.
I recommend querying the database once for the whole adapter. Currently you are sending three queries for each row and none of them are cached. Reading from the drive is a slow process, Cursors are designed to fetch a large amount of data very efficiently and CursorAdapters are designed to use the least resources possible. Using one Cursor and a CursorAdapter will allow users to scroll without noticing any performance loss.
There is also a library called ORMLite which allows you to manipulate database quite easily and you get back a list, which means you load the array first and you don't have any scrolling performance issue. This in combination with lazy loading sections of the database will allow you to seamlessly increase the performance of the scroll.

Best practices for joining tables and notifying ContentObservers in Android ContentProvider

I have a ContentProvider which handles all the data insertion and retrieval related to my application, I'm following the pattern suggested by Virgil Dobjanschi on Google I/O. I am using the first pattern.
My problem is that I have a logical entity that was represented by multiple tables in the database.
For example, I have an Articles table and an ArticleExtras table. Articles represents the article itself, while ArticleExtras represents addtional information about certain Article, like number of comments.
I used CursorAdapter in the UI to display the Article title and the number of comments of that Article in one row of ListView.
To implement that, I added a left outer join ArticleExtras on statement in my ContentProvider query method for Article table, in order for CursorAdapter to get ArticleExtras along with the Article itself.
When new Articles are fetched from the web, I insert it into the database via ContentProvider, and then the CursorAdapter got notified and update the UI, this part worked as expected.
But when I fetched the number of comments (ArticleExtras), I want the same CursorAdapter, which is watching for changes in the content://com.myapp.content/Articles, to be notified, too, so I can update my row in the ListView.
My current implementation is like this: After inserting ArticleExtras into the database, I start a new query to check if Articles table has any rows that is related to the ArticleExtras I just inserted. If so I'll make a new uri for that Article( for example: content://com.myapp.cotent/Articles/123), and call getContext().getContentResolver().notifyChange(uri, null), so the corresponding CursorAdapter that is watching for changes of this Article will get notified.
Is the approach correct, or is there any better way to implement what I want?
Checkout ContactsProvider2, in it they set the notification uri to the AUTHORITY_URI which appears to be a catch all for the other URIs in the provider. I had the same probem and I have tried this myself for a provider with multiple tables and joins on those tables, and it works fine.

Categories

Resources