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...
Related
Having fragment like below, in normal flow the onCreateLoader and onLoadFinished is called in pair.
But when the datasource (the database) content is changed, and since the loader is monitoring the data change the loader will issue another call to onLoadFinished() with new data filled in cursor.
But In my case it does not want to change the current cursor in use, so don't want the loader deliver the updated cursor vis another onLoadFinished call, or disable the loader's monitoring part.
Is there a way to do it?
AFragment extends Fragment implements LoaderCallbacks<Cursor> {
protected void startSupportLoaderManager() {
getActivity().getSupportLoaderManager()
.initLoader(LOADER_ID, null, this);
}
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return createLoader(getActivity(), id, args, null);
}
#Override
public void onLoaderReset(Loader<Cursor> arg0) {
if (mAdapter != null) {
mAdapter.resetCursor();
}
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
...
}
}
EDIT:
Kinda of knowing this may work, but feel still missing some dot. Here is what the thought:
In the implementation of ContentProvider, for insert(), update(), delete() we do
getContext().getContentResolver().notifyChange(uri, null);
And in the CursorLoader it did cursor.registerContentObserver(mObserver); in
public Cursor loadInBackground() {
Cursor cursor = getContext().getContentResolver().query(mUri, mProjection, mSelection,
mSelectionArgs, mSortOrder);
if (cursor != null) {
// Ensure the cursor window is filled
cursor.getCount();
cursor.registerContentObserver(mObserver);
}
return cursor;
}
So it in some way is monitoring the Uri data source change, same as we do on cursor
cursor.setNotificationUri(getContext().getContentResolver(), uri);
So if we could provide a Uri, which could used for insert/update/delete, but is different than the Uri we give to the Loader, that would still do the inert/update/delet operation but the loader will not be notified because of the Uri is different.
So basically, the loader will use different Uri than the Uri the other data operation using.
Not sure If understanding how the Loader's monitoring with the content Uri is correct. Maybe there is better way of doing it?
Any suggestion is appreciated.
If you want to stop the loader, you can use:
getLoaderManager().destroyLoader(LOADER_ID);
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)
Can someone just explain to me what is runQueryOnBackgroundThread as I already read through some sources but still not understand with it?
#Override
public Cursor runQueryOnBackgroundThread(CharSequence constraint){
FilterQueryProvider filter = getFilterQueryProvider();
if (filter != null){
return filter.runQuery(constraint);
}
Uri uri = Uri.withAppendedPath(
ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI, Uri.encode(constraint.toString()));
return content.query(uri, CONTACT_PROJECTION, null, null, null);
}
A handle of my Activity in the Adapter and the runQuery call in the filter makes a call to startManagingCursor on the Activity whenever the runQuery is called. This is not ideal because a background thread is calling startManagingCursor and also there could be a lot of cursors remaining open until the Activity is destroyed.
I added the following to my Adapter which has a handle on the Activity it is used within
#Override
public void changeCursor(Cursor newCursor) {
Cursor oldCursor = getCursor();
super.changeCursor(newCursor);
if(oldCursor != null && oldCursor != newCursor) {
// adapter has already dealt with closing the cursor
activity.stopManagingCursor(oldCursor);
}
activity.startManagingCursor(newCursor);
}
This makes sure that the current cursor used by the adapter is also managed by the activity. When the cursor is closed by the adapter management by the activity is removed. The last cursor held by the adapter will be closed by the activity by way of it still be managed by the activity.
I can use a managedQuery like this:
Activity a = (Activity) context;
cursor = a.managedQuery(uri, null, null, null, null);
And once I do, I have a cursor that I can step through however I want.
However, using a CursorLoader, when a new CursorLoader is created the onCreateLoader call back method is called. The onCreateLoader call back returns a CursorLoader. How do I get a reference to the cursor so I can step though it, as in managedCursor.
I missing the boat here, appreciate any direction.
You need to also implement onLoadFinished, this method gives you the Cursor when the asynchronous load has finished
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
myadapter.swapCursor(cursor);
}
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...