I have an app that uses a cursor to select data via rawQuery from an SQLite DB to populate a ListView in Android. Every time the user clicks on a listview item I create a new instance of Activity to re-populate listview.
Is it better to call cursor.close() and db.close() to avoid memory problems? I actually have db.close() in OnDestroy() of my activity.
You can close the cursor once you have retrieved the values for that particular object inside your method.
btw...You don't have to recreate a listview every time for a user click event. Just notify that there is some change in data of your adapter that has been set on the listview.
Something like
youradaptername.notifyDataSetChanged();
This should repopulate contents inside ur listview automatically.
Well if you are creating a new instance every time of the same Activity (though I am not sure its a good programming practice). You can close the cursor as soon as your have finished traversing / iterating through the source of the listview.
Example:
A sample implementation would be something like
//Pre cursor code
startManagingCursor(cursor);
if (cursor.moveToFirst()) {
do {
if (cursor.getString(0).equals(value)) {
cursor.close();
a = true;
return a;
}
} while (cursor.moveToNext());
}
//Close cursor here, when its work is complete
cursor.close();
//Post cursor code ...
Related
I came across this info that not closing the cursor or database gobbles up memory. So as i use the cursor across a hundred test cases I might forget to close it somewhere. Can I declare a single cursor and reuse it again and again making different references and finally closing it in the OnDestroy() method.
Eg. Cursor a;
//Another function
a = as.rawQuery("select * from verse"+k, null);
//Another function
a = bs.rawQuery("select * from hello", null); //Another database
//Another function
a = cs.rawQuery("select * from chapter", null); //Another database
//OnDestroy()
a.close();
Is it a feasible solution??
You cannot use a single cursor; each rawQuery call creates a new cursor object.
When you assign a reference to the new cursor object to a, the old object is not closed (and you cannot anymore reach it).
To ensure that close is always called, use try/finally:
Cursor c = db.rawQuery(...);
try {
... read data ...
} finally {
c.close();
}
If you have a helper function that returns a cursor, do this in the calling function.
I have two activities: one NoteListActivity which inherits from ListActivity, and I used SimpleCursorAdapter as its adapter where the cursor is obtained as below:
public Cursor getAllNotesCursor() {
String selectQuery = "SELECT _id , title, content FROM " + NOTE_TABLE_NAME;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
return cursor;
}
The another activity NoteEditorActivity is responsible for creating new note, there is a save action there and on click I will add a new note in the SQLite database then call finish to the NoteListActivity.
The problem is that the NoteListActivity didn't get updated with new note, do you know the best practice to achieve this?
One solution I can thought of is starting NoteEditorActivity by calling startActivityForResults then call cursor requery in onActivityResult, I don't know whether there is better solution?
startActivityForResults is good, but why not try to override onResume() method, with yourAdapter.notifyDataChange()
#Override
public void onResume() {
...
yourAdapter.notifyDataSetChanged();
}
Of course you have to add yourAdapter on your field class.
Whatever you are doing in onCreate method that is affecting UI to draw or show note by fetching from database.
Don't do it in onCreate.
DO IT IN onResume
#Override
public void onResume(){
//fetch here, do other operation, or set layout here
}
notifyDataSetChanged Update List View Adopter
http://androidadapternotifiydatasetchanged.blogspot.in/
try following steps..
use startActivityForResult() inside NoteListActivity to start NoteEditorActivity.
set RESULT_OK in save button click event
Populate list in onActivityResult() of NoteListActivity
I have a database helper class that gets all of the rows of a table from a SQLite database. In the onCreate of my Main Activity I am calling that method and populating a ListView with that data.
I have a separate Activity that is getting info from a network and inserting rows into the database. After that is done I am calling finish() and it returns to the Main Activity.
When it returns the inserted rows are not being displayed in the ListView. From debugging, it appears that they are not being returned from the database helper method.
If I close and relaunch the app, the rows show up. If I change the screen orientation, they show up.
Here is my database helper method:
public List<Object> getAllObjects() {
List<Object> objects = new ArrayList<Object>();
Cursor cursor = database.query(SQLiteHelper.TABLE_OBJECT,
allColumns, null, null, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Object object = cursorToObject(cursor);
objects.add(object);
cursor.moveToNext();
}
cursor.close();
return objects;
}
Your main Activity is not newly created, you return to an old instance. In your case, put the update code in the onResume method of your main Activity, not in the onCreate method.
See the Activity lifecycle for further details on when code is executed.
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
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...