I sometimes see this error in my logcat output,
Cursor: invalid statement in fillWindow().
It sometimes happens when I press the back key and then it goes to the default Android listview before going to my custom listview.
What does it mean? How do I solve it? Because it does not point to any line of code where the problem is coming from.
When dealing with ListActivities, this issue has to do with the Cursor objects, CursorAdapter objects, and Database objects not being closed properly when the Activity stops, and not being set properly when the Activity starts or resumes.
I had to make sure that I closed my SimpleListAdapter, my Cursors, and then my Database objects in that respective order, in the onStop method of the Activity that is called when the TabActivity resumes.
I had already been closing the Cursor and Database objects, but had not been closing my SimpleListAdapter Cursor.
/**
* onStop method
*
* Perform actions when the Activity is hidden from view
*
* #return void
*
*/
#Override
protected void onStop() {
try {
super.onStop();
if (this.mySimpleListAdapterObj !=null){
this.mySimpleListAdapterObj.getCursor().close();
this.mySimpleListAdapterObj= null;
}
if (this.mActivityListCursorObj != null) {
this.mActivityListCursorObj.close();
}
if (this.myDatabaseClassObj != null) {
this.myDatabaseClassObj.close();
}
} catch (Exception error) {
/** Error Handler Code **/
}// end try/catch (Exception error)
}// end onStop
It is of utmost importance that you close the Cursors, Databases, DBHelpers in the right order.
for e.g.
for the given code below.
DBHelper dbhelper = new DBHelper();
SQLiteDataBase db = dbhelper.getWritableDatabase();
Cursor c = db.query(/*some parameters*/);
the order of closing should be like:
c.close();
db.close();
dbhelper.close();
Otherwise different errors keep on spawning and the developer does not even come to know about it. "Cursor: invalid statement in fillWindow()" being one of such errors.
Maybe this can help you: http://www.ragtag.info/2011/feb/1/database-pitfalls/
It seems that calls to getReadableDatabase and getWritableDatabase returns the same connection to the database (even if you made several calls to them).
So, any call to close() on any of them will close both connection(s).
If you tries to use a cursor later, you'll get the nice 'Invalid statement', since the connection which the cursor relies on, is already closed.
If you are using a custom Class instance e.g. Model m that holds a DatabaseManager, which in turn holds a SQLiteDatabase: Model->DatabaseManager->SQLiteDatabase
Then, if you do a query to m (which does the appropiate delegations) and then you do something like m.close() (which actually closes the SQLiteDatabase) and after that you try to use the Cursor you will get that error.
The solution is: first use the cursor and then close the Db.
My response is based in the 2 existing so far, that inspired me to solve the problem.
I am still having issues with the 'Invalid statement in fillWindow()' error.
I have narrowed the issue down to the SimpleCursorAdapter cursor for my ListView.
For example, if I am in the listview for an Activity A, and I close the cursor before starting a new Activity B, I don't get the 'Invalid statement in fillWindow()' when I return to Activity A.
However, before Activity B loads, I see the list from Activity A's listview disappear on the screen, and the 'No Records Found' message is displayed briefly before the screen is hidden, before Activity B's screen is shown.
How can I gracefully resolve this issue?
EDIT:
I actually figured this out this morning. I added
this.stopManagingCursor(this.myListCursor);
to the onPause method in my ListActivity classes, and that resolved the 'Invalid statement in fillWindow()' error.
Related
I'm doing simply queries like:
Cursor c = databaseHelper.getReadableDatabase().rawQuery("select " + MyTable.TABLE_FIELD.getColumnName() + " from " + MyTable.TABLE_NAME, null);
after that I extract the data needed and populate some views.
c.close();
Everything works as expected. I have a share intent to send an e-mail with some of the information shown. The java.lang.IllegalStateException is thrown after the e-mail app sends the e-mail or closes and my activity is brought back to "life".
This exception is only thrown in this situation, if I send it to background and then call it again this exception isn't thrown. I even have an intent to open youtube and play a video and this exception isn't thrown...
I really don't know why this is happening.
Thanks for your time.
ps: I've tried //c.close(); but it didn't work either.
You could use c.requery() but it was deprecated in API level 11.
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.
Performs the query that created the cursor again, refreshing its
contents. This may be done at any time, including after a call to
deactivate(). Since this method could execute a query on the database
and potentially take a while, it could cause ANR if it is called on
Main (UI) thread. A warning is printed if this method is being
executed on Main thread.
I found what was causing this problem. Some of the e-mail's information was fed by another cursor from an "utils" class. This cursor was closed after providing the information to the e-mail and this was causing the problem. Instead of c.close(); I changed to c = null; and now it's working.
Probably not the best solution, but for now it's working.
Thanks.
This worked for me..
public void onBackPressed() {
Intent i = new Intent(getApplicationContext(), ClassName.class);
startActivity(i);
finish();
super.onStop();
}
I'm loading some results from a database using a loaderManager. Unfortunately, the following code produces a StaleDataException after rotating the device:
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor)
{
// If we've returned results, then pass them and the webSearches cursor along to be displayed
if(cursor.moveToFirst())
{
// Get a cursor containing additional web searches and merge it at the end of the results cursor
MatrixCursor searchesCursor = getWebSearchesCursor(searchTerm, false);
Cursor[] cursors = { cursor, searchesCursor };
// TODO: Figure out why merging cursors here causes staledataexception after rotation
Cursor results = new MergeCursor(cursors);
// Display the cursor in the ListView
adapter.changeCursor(results);
}
// If no results were returned, then return suggestions for web searches
else
{
// Get a cursor containing additional web searches
MatrixCursor noResults = getWebSearchesCursor(searchTerm, true);
adapter.changeCursor(noResults);
}
// Show the listView and hide the progress spinner
toggleListView(SHOW);
}
The call to getWebSearchesCursor() returns a MatrixCursor with some additional search prompts to accompany any returned results. I discovered that changing adapter.changeCursor(results) to adapter.changeCursor(cursor) fixes the error, so it looks like merging a MatrixCursor to the returned cursor produces the error.
My question is, why?
If any results are returned, I'd like to be able to add additional items to the returned cursor so the user has the option to perform their search on a couple of websites. Is there a better way to merge cursors so that I don't get this exception after rotation?
If you've started using swapCursor() instead of changeCursor() everywhere, then I hope you've also started handling cursor closing in those places.
changeCursor() will close the old cursor, this is intentional and works flawlessly when you're just directly using the cursor provided by onLoadFinished(). It is done this way so you don't have to worry about closing it.
When you rotate your device, the android system will check that the cursor it sent you last time has not yet been closed, and sends it again rather than spending resources on creating a new one. Your code wraps this cursor in a new instance of a MergeCursor which gets passed to changeCursor(), which sees that this is not the same object it got before, and decides to close the old instance. Since MergeCursor only wraps the cursors you pass it, rather than copying the data in them, your new instance now contains (at least) one closed cursor.
To correctly handle this you'll need to write some code of your own that checks if the cursor you get through onLoadFinished() is the same as one you have in your current MergeCursor instance, and only close the existing instance if you are getting a new cursor. Of course you'll also need to keep track of the other cursors wrapped in the same MergeCursor instance.
This issue came up again a couple of days ago and I was fortunate enough to stumble upon a solution.
I found out that I should have been using swapCursor() instead of changeCursor(). According to the Android docs:
Swap in a new Cursor, returning the old Cursor. Unlike changeCursor(Cursor), the returned old Cursor is not closed.
...
If the given new Cursor is the same instance is the previously set Cursor, null is also returned.
That last part seemed to be the key. The error mentioned in the question above could be traced back to the CursorAdapter choking on the merged cursor because it was closed when it tried to redraw the fragment after a rotation. By using swapCursor() instead, the CursorAdapter was able to reuse the "old" merged cursor instead of questioning its validity and throwing a StaleDataException.
I'm making some suppositions here; perhaps someone more knowledgeable in the inner-workings of Android could confirm or deny my reasoning.
I have an class that extends ExpandableListActivity. To fill the data for this list, I'm using a class that extends SimpleCursorTreeAdapter. I have a group (top-level) cursor that looks something like this:
mGroupCursor = ((MyApplication)getApplication()).
getDatabaseHelper().getGroupCursor();
startManagingCursor(mGroupCursor);
Of course, I also need to provide the child cursors:
#Override
protected Cursor getChildCursor(Cursor groupCursor)
{
Cursor childCursor = ((MyApplication)getApplication().
getDatabaseHelper().
getChildCursorForGroup(groupCursor.getInt(mGroupIdIndex));
startManagingCursor(childCursor);
return childCursor;
}
I was under the impression that I could call startManagingCursor on these cursors and everything would be taken care of. Apparently that is not the case, because leaving the list view and coming back with the back button throws an exception, "unable to resume activity: attempting to requery closed cursor" or something like that.
Removing the calls to startManagingCursor() fixes the problem, although I think that this is not really the right way to do things because in this case I'm not closing the cursors anywhere.
What is the recommended way to handle these cursors? I'm looking for something simple and lightweight. My database is a local SQLite database and is relatively small, so as long as the performance isn't unreasonably slow it's not an issue for me.
startManagingCursor is obsolete. I'd look at using an AsyncTask to get the cursors in the background, and set the observers yourself.
I am trying to make my app rotation friendly, but I am having some problems saving the cursor.
The cursor holds about 13k+ rows of data displayed in a ListView, and thus would take quite a while if I would do a requery every time the configuration changes. In my onRetainNonConfigurationInstance(), I am returning my Cursor and then retrieving it through getLastNonConfigurationInstance().
However, my retrieved cursor seems to be closed already, and thus my adapter cannot render the list anymore. From what I understand, the cursor was closed since onDestroy() automatically closes all cursors.
I save the Cursor like this:
#Override
public Object onRetainNonConfigurationInstance() {
return myCursor;
}
And retrieve it like this:
myCursor = (Cursor)getLastNonConfigurationInstance();
if (myCursor == null) {
// Do some stuff here (access DB, etc)
} else { // we are returning from configuration change
// Feed the cursor to the adapter
}
I am pasting the stack trace if someone wants to look at it:
01-25 16:57:45.637: ERROR/AndroidRuntime(12976): android.database.StaleDataException: Access closed cursor
01-25 16:57:45.637: ERROR/AndroidRuntime(12976): at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:217)
01-25 16:57:45.637: ERROR/AndroidRuntime(12976): at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:41)
01-25 16:57:45.637: ERROR/AndroidRuntime(12976): at com.test.sample.helper.DictionaryAdapter.bindView(DictionaryAdapter.java:35)
[........More ListView-related errors here..........]
I stepped through the code and I found out that as far as onRetainNonConfigurationInstance(), the cursor is still open, but after getting it through getLastNonConfigurationInstance() it is already closed.
How can I make my Cursor survive the orientation change? Thank you for the help!
EDIT:
Based on Romain's answer, I commented out all my startManagingCursor()s. I should have connected the dots and thought about it! Anyway, my app now survives one rotation, but flipping it back to the original orientation still crashes it. Continuing my debugging and will let you know what I find out.
EDIT2:
I think I may have found what is causing the new errors. I have implemented a FilterQueryProvider which returns a new Cursor. What I did was assign the results of that filter to my original Cursor. Seems to work so far.
You are probably using a managed cursor. Managed cursors are automatically closed when the Activity is destroyed. You should switch to an unmanaged Cursor.
Just add this attribute in your activity tag in the manifest file
android:configChanges="orientation|keyboardHidden"
it will resolve this
no need to implement anything else
:)
It helped me though
i'm getting a "Finalizing a Cursor that has not been deactivated or
closed" error on this piece of code.
The code is used to fill a listview.
Since it's a non-fatal error , there is no crash and all seems to works
fine..but i don't like the error.
If i close the cursor at the end of this code..the listview stay's
empty.
if i close the cursor in onStop , i get the same error.
How do i fix this??
private void updateList() {
DBAdapter db = new DBAdapter(this);
db.open();
//load all waiting alarm
mCursor=db.getTitles("state<2");
setListAdapter(new MyCursorAdapter(this, mCursor));
registerForContextMenu(getListView());
db.close();
}
error :
E/Cursor ( 2318): Finalizing a Cursor that has not been deactivated
or closed. database = /data/data/xxxxxxxxxxxxxxx.db, table = alerts,
query = SELECT _id, alert_id,
E/Cursor ( 2318):
android.database.sqlite.DatabaseObjectNotClosedException: Application
did not close the cursor or database
object that was opened here
E/Cursor ( 2318): at
android.database.sqlite.SQLiteCursor.<init>(SQLiteCursor.java:210)
E/Cursor ( 2318): at
android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:
53)
E/Cursor ( 2318): at
android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:
1345)
E/Cursor ( 2318): at
android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:
1229)
....
....
You should not be getting that message if you close the Cursor in onStop() or onDestroy(). Please try that again. Or, call startManagingCursor() after you get the Cursor from your query, and Android will close the Cursor on its own.
Scott,
I ran into the same problem as you. Before you close your database, i.e. "db.close()," make sure that your cursors are closed first, i.e. "mCursor.close()"
Like so:
private void updateList()
{
DBAdapter db = new DBAdapter(this);
db.open();
//load all waiting alarm
mCursor=db.getTitles("state<2");
setListAdapter(new MyCursorAdapter(this, mCursor));
registerForContextMenu(getListView());
// Let's close the cursor.
mCursor.close();
db.close();
}
You mentioned that if you closed your cursor your list view stays empty. I recommend you pass the information over to a class and copy it over (allocate the memory) then close the cursor.
When a query returns a cursor it is actually positioned "before" the first
record in the cursor. An adapter will attempt do a 'getItem' at the first
element so it will fail as the cursor is not positioned at any.
In my base adapters I do a cursorMoveToPosition on the getViews. This seems
to eliminate the need for the movefirst.
Do not use startManagingCursor() since that is no longer the recommended approach. The issue occurs because a cursor / DB connection is still not closed by the time the finalizer gets to this object. You can avoid that by either allowing a loader to manage the cursor or by tracking all cursor / DB / SQLiteOpenHelper connections yourself and cleaning up after them.
Using a Loader is fairly cumbersome and requires a lot of moving parts for it to work in conjunction with say a list view. On the other hand tracking your cursor and database connections is prone to human error. If the number of cursor / DB objects is low, I'd recommend the latter solution. If not, let a loader handle your connections.
Close the cursor object wherever you are creating it.
When you create a cursor object and done with traversing through a SQLite table then close it after it has been used. This closing of cursor prevents exception in logcat.
You will not get any exception related to finalizing the cursor that left opened.
This fixed same issue in my Application.
I struggled with this problem for two days. I was trying to get sample code working which passed a cursor, returned from a database query, directly to List Adapter - no interim adapter. It refused to work - just displayed a blank screen - until I invoked 'moveToFirst()' on the cursor before passing it to the ListAdapter. Go figure! When I comment this out, it breaks.
Just thought that I'd share this to save people the same struggles that I had.
If anyone can shed some light on why this is so, I'd appreciate it. I haven't had to invoke moveToFirst on cursors up to now, to get them to perform properly.
Just had the same problem and thought to let you know - just in case....
I accidentally called my fetch routine two times and hereby "lost" the resulting cursor of the first call. This caused the error.
I too have been having issues with the closing the cursor:
Closing the cursor right after setting the list view's adapter causes the cursor to close before the data gets displayed.
Can't use startManagingCursor to manage the cursor because it has been deprecated.
The new cursorLoader replacement for startManagingCursor seems to be overkill.
Moving the cursor's position as suggested did not work.
Making the task an inner class of the activity and closing the cursor in the activity's onDestroy method works sometimes but not all the time.
Making the task an inner class of the activity and closing the cursor in the activity's onStop method seems to be working.
I also found out that I can close the database and the sqlite open helper before closing the cursor. I can even close them right after setting the list view's adapter. The data will still display.
startManagingCursor(cursor);
This has fixed my problem