When to close Cursor used in SimpleCursorAdapter - android

I'm using a SimpleCursorAdapter to display results in a ListView but since I've got to query my database lots of times during a search (using the SearchView widget) it worries me that the cursor might be left opened.
This is how I query my database and show the results in a ListView:
class SearchCustomers extends AsyncTask<String,Void,Cursor>{
#Override
protected Cursor doInBackground(String... params) {
//get the query
String query=params[0].toLowerCase(Locale.getDefault());
Cursor cursor=mDB.searchCustomersByName((query != null ? query : "####"));
return cursor;
}
#Override
protected void onPostExecute(Cursor result) {
if (result != null) {
String[] from = new String[] { QuickOrderDB.ID,
QuickOrderDB.NAME,
QuickOrderDB.ADDRESS,
QuickOrderDB.PHONE_NUMBER };
int[] to = new int[] { R.id.customerIDTextView,
R.id.customerNameTextView,R.id.customerAddressTextView ,
R.id.customerPhoneTextView };
SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter(SearchCustomersActivity.this,
R.layout.results_customer_item, result, from, to);
mResultsListView.setAdapter(cursorAdapter);
}
}
}
I have tried many things to close the cursor, but even If I close it after mResultsListView.setAdapter(cursorAdapter); the result is always the same: an empty ListView.
I've already seen a couple of questions in which it is mentioned that the cursor will be closed automatically, but I want to make sure this is true.
Is there any official documentation about this? Does the SimpleCursorAdapter really close the cursor automatically??
Thanks in advance.

You need to close your cursor once you are done with it. Closing it after setAdapter() call would prevent the adapter from accessing the data. Hence a better place to close the cursor would be during current activities tear down life cycle stages such as onPause() or onStop(). (onDestroy() should not be used as Android run-time does not guarantee calling it. I think on latest version onStop() is guaranteed)
I don't think SimpleCursorAdapter adapter automatically closes the cursor automatically. The official document mentions that changeCursor() automatically closes the old cursor, so another option could be to change your cursor after search.
http://developer.android.com/reference/android/widget/CursorAdapter.html#changeCursor(android.database.Cursor)

It's better if you get the Cursor using a CursorLoader instead of an AsyncTask.
The Loaders are synched to the Activity/Fragment lifecycle via the LoaderManager, and the system will close the Cursor provided by the CursorLoader automatically for you when it's needed.

You should close the cursor in your fragment or activity's onPause() callback. After the activity is paused it's possible that older Android systems will delete the app to free memory.
This implies that you need to reestablish the cursor in the corresponding onResume() callback.

Don't create a variable for the cursor, just add the db query directly into the constructor as the argument c, db.query() or a method that holds the desired query), this seems to work.
SimpleCursorAdapter (Context context,
int layout,
Cursor c,
String[] from,
int[] to,
int flags)

Related

Hacky way of refreshing CursorAdapter cursor

Trying to refresh a ListView populated by CursorAdapter, I would have thought something like this would work:
SimpleCursorAdapter listAdapter;
#Override
protected void onRestart() {
super.onRestart();
listAdapter.swapCursor(listAdapter.getCursor());
}
In that it swaps cursor from old one to new one and the ListView gets the new data. Why doesn't this way work and what is the alternative?
Slightly more information, swapCursor() is supposed to return the old cursor but in the above it returns null.
EDIT:
I know why it doesn't work. swapCursor() checks if the new cursor and the old cursor are the same, if it is then it does nothing:
public Cursor swapCursor(Cursor newCursor) {
if (newCursor == mCursor) {
return null;
}
And changeCursor calls swapCursor so can't use that. Now to hack that into working...

How do I close my Cursor when using rawQuery and CursorAdapter

I am using a rawQuery in my DB class and returning the cursor to my adapter which is a CursorAdapter which I use with a custom ListView item.
Is this cursor automatically closed after the view is painted in the screen or how to manage this cursor? what is the best practice in this scenario ?
If I close this cursor in DB class I am not able to access them from my adapter.
Thanks for your time and effort in helping me.
EDITing to add some code snippets for better understanding
This is my activity code:
calAdapter = new CalendarListAdapter(context, dbHelper.getAllCalendars());
drawerList.setAdapter(calAdapter);
This is my cursor
public Cursor getAllCalendars() {
String query = "SELECT calendarId as _id, calendarName, calState, calShow FROM "
+ TABLE_CALENDAR_LIST;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(query, null);
return cursor;
}
You can close the old cursor after you set the new one. swapCursor() returns the old Cursor, returns null if there was not a cursor set, also returns null if you try to swap the same instance of the previously set cursor. Knowing that, you can try something like this:
Cursor oldCursor = yourAdapter.swapCursor(newCursor);
if(oldCursor != null)
oldCursor.close();
Note that when you are using a Loader (LoaderManager.LoaderCallbacks), the framework is going to close the old cursor. This is what the documentation says:
onLoadFinished:
The loader will release the data once it knows the application is no
longer using it. For example, if the data is a cursor from a
CursorLoader, you should not call close() on it yourself. ...
Cursor does not get closed automatically. You need to close it. In older API you could register Cursor at Activity by calling Activity.startManagingCursor(). In this case Activity takes responsibility for managing it. In newer API you should better use LoaderManager.

AutoCompletetextView with CursorLoader

I´m changing my application to use CursorLoader instead of startManagingCursor....
And I have one AutoCompleteTextView, SimpleCursorAdapter and in my setFilterQueryProvider implementation, in "runQuery" method I have
mProdutoAdapter.setFilterQueryProvider(new FilterQueryProvider() {
#Override
public Cursor runQuery(CharSequence constraint) {
Cursor cursor = mVendasDb.getProdutos(constraint.toString());
startManagingCursor(cursor);
return cursor;
}
});
How can I change this part to make it works with CursorLoader?
Should I discard setQueryFilterProvider and use TextWatcher in the AutocompleteTextView and then call getLoaderManager().restartLoader(0, bundleFilter, this)??? I have no idea how to proced!
bundleFilter would have the text came from textwatcher.
Is it ok using this aproach? Is it the best to do in this case?
This is a fine approach, according to the android developer website :
Restarting a Loader :
To discard your old data, you use restartLoader(). For example, this implementation of SearchView.OnQueryTextListener restarts the loader when the user's query changes. The loader needs to be restarted so that it can use the revised search filter to do a new query:

Android: how to use CursorAdapter?

I have a database, a ListView, and a CustomCursorAdapter that extends CursorAdapter. A menu button adds an item to the database. I want the ListView to update and show this change. Normally it doesn't show this new item until i go to the homescreen and reopen the application.
I did eventually get it to work by calling cursor.requery() or mCustomCursorAdapter.changeCursor(newCursor) whenever I added a new item, but when I set autoRequery to false in the CursorAdapter constructor, it worked just the same. Why does it update correctly when autoRequery is set to false?
Am I using CursorAdapter correctly? What is the standard way of keeping the list updated with the database? And what does autoRequery do?
The idiomatic and imho correct way to automatically update Cursors is to call Cursor#setNotificationUri when they are created and before they are handed off to whatever requested them. Then call ContentResolver#notifyChange when anything in that Cursor's Uri's namespace changes.
For example, suppose you were creating a simple mail application and you wanted to update when new mail arrived but also provide various views on the mail. I'd have some basic Uri's defined.
content://org.example/all_mail
content://org.example/labels
content://org.example/messages
Now, say I wanted to get a cursor that gave me all mail and be updated when new mail arrives:
Cursor c;
//code to get data
c.setNotificationUri(getContentResolver(), Uri.parse("content://org.example/all_mail");
Now new mail arrives so I notify:
//Do stuff to store in database
getContentResolver().notifyChange(Uri.parse("content://org.example/all_mail", null);
I should also notify all the Cursors that selected for labels this new message met
for(String label : message.getLabels() {
getContentResolver().notifyChange(Uri.parse("content://org.example/lables/" + label, null);
}
And also, maybe a cursor is viewing that one specific message so notify them as well:
getContentResolver().notifyChange(Uri.parse("content://org.example/messages/" + message.getMessageId(), null);
The getContentResolver() calls happen where the data is accessed. So if it's in a Service or ContentProvider that is where you setNotificationUri and notifyChange. You should not be doing that from where the data is accessed, e.g., an Activity.
AlarmProvider is a simple ContentProvider that uses this method to update Cursors.
I created next method for ListView updating:
/**
* Method of refreshing Cursor, Adapter and ListView after database
* changing
*/
public void refreshListView() {
databaseCursor = db.getReadableDatabase().query(
CurrentTableName,
null,
null,
null,
null,
null,
"title"+SortingOrder);
databaseListAdapter = new DomainAdapter(this,
android.R.layout.simple_list_item_2,
databaseCursor,
new String[] {"title", "description"},
new int[] { android.R.id.text1, android.R.id.text2 });
databaseListAdapter.notifyDataSetChanged();
DomainView.setAdapter(databaseListAdapter);
}
end calls it each time after some changing in database

Android SimpleCursorAdapter doesn't update when database changes

I have an Android ListActivity that is backed by a database Cursor through a SimpleCursorAdapter.
When the items are clicked, a flag field in the coresponding row in the database is toggled and the view in the list needs to be updated.
The problem is, when the view that's updated goes off screen and is recycled, the old value is displayed on the view when it returns into view. The same thing happens whenever thr list is redrawb (orientation changes, etc).
I use notifydatasetchanged() to refresh the cursor adapter but it seems ineffective.
How should I be updating the database so the cursor is updated as well?
Call requery() on the Cursor when you change data in the database that you want reflected in that Cursor (or things the Cursor populates, like a ListView via a CursorAdapter).
A Cursor is akin to an ODBC client-side cursor -- it holds all of the data represented by the query result. Hence, just because you change the data in the database, the Cursor will not know about those changes unless you refresh it via requery().
UPDATE: This whole question and set of answers should be deleted due to old age, but that's apparently impossible. Anyone seeking Android answers should bear in mind that the Android is a swiftly-moving target, and answers from 2009 are typically worse than are newer answers.
The current solution is to obtain a fresh Cursor and use either changeCursor() or swapCursor() on the CursorAdapter to affect a data change.
requery is now deprecated. from the documentation:
This method is deprecated.
Don't use this. Just request a new cursor, so you can do this asynchronously and update your list view once the new cursor comes back.
after obtaining a new cursor one can use theadapter.changeCursor(cursor). this should update the view.
In case of using loader and automagically generated cursor you can call:
getLoaderManager().restartLoader(0, null, this);
in your activity, just after changing something on a DB, to regenerate new cursor.
Don't forget to also have event handlers defined:
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
CursorLoader cursorLoader =
new CursorLoader(this,
YOUR_URI,
YOUR_PROJECTION, null, null, null);
return cursorLoader;
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
adapter.swapCursor(data);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
I am not clear if you set the autoRequery property of CursorAdapter to true.
The adapter will check the autoRequery property; if it is false, then the cursor will not be changed.
requery() is already deprecated, just implement the simple updateUI() method like this in your CursorAdapter's child class and call it after data updates:
private void updateUI(){
swapCursor(dbHelper.getCursor());
notifyDataSetChanged();
}
It's easy.
private Db mDbAdapter;
private Cursor mCursor;
private SimpleCursorAdapter mCursorAd;
.....................................
//After removing the item from the DB, use this
.....................................
mCursor = mDbAdapter.getAllItems();
mCursorAd.swapCursor(mCursor);
Or use CursorLoader...

Categories

Resources