I'm currently using a CursorLoader inside a Fragment with the Android Compatibility Library. Almost every time, on the Droid 2 (I'm unable to reproduce on the Nexus One or the Sensation), it seems that the cursors are being closed too soon, which is causing various errors. Unfortunately the traces don't point anywhere helpful in my code (since the issue is why the Cursor is being closed, not when Android notices), so I'm really struggling to figure out what is going wrong. Has anyone else encountered this problem? (or have ideas what might be happening).
Some stack traces:
java.lang.IllegalStateException: Cursor is closed
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:278)
at android.database.sqlite.SQLiteCursor.onMove(SQLiteCursor.java:255)
at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:187)
at android.database.CursorWrapper.moveToPosition(CursorWrapper.java:187)
at android.support.v4.widget.CursorAdapter.getItemId(CursorAdapter.java:226)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:1721)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:143)
at android.app.ActivityThread.main(ActivityThread.java:4717)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
at dalvik.system.NativeStart.main(Native Method)
java.lang.IllegalStateException: Cursor is closed
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:278)
at android.database.sqlite.SQLiteCursor.onMove(SQLiteCursor.java:255)
at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:187)
at android.database.CursorWrapper.moveToPosition(CursorWrapper.java:187)
at android.support.v4.widget.CursorAdapter.getItemId(CursorAdapter.java:226)
at android.widget.AdapterView.getItemIdAtPosition(AdapterView.java:745)
at android.widget.AdapterView.setSelectedPositionInt(AdapterView.java:1081)
at android.widget.AbsListView.onTouchEvent(AbsListView.java:2207)
at android.widget.ListView.onTouchEvent(ListView.java:3377)
at android.view.View.dispatchTouchEvent(View.java:3766)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:897)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:936)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:1800)
at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1140)
at android.app.Activity.dispatchTouchEvent(Activity.java:2105)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:1784)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1794)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:143)
at android.app.ActivityThread.main(ActivityThread.java:4717)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
at dalvik.system.NativeStart.main(Native Method)
java.lang.RuntimeException: Unable to pause activity : java.lang.IllegalStateException: Cursor is closed
at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3438)
at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3395)
at android.app.ActivityThread.handlePauseActivity(ActivityThread.java:3378)
at android.app.ActivityThread.access$2700(ActivityThread.java:129)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2124)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:143)
at android.app.ActivityThread.main(ActivityThread.java:4717)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalStateException: Cursor is closed
at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:278)
at android.database.sqlite.SQLiteCursor.onMove(SQLiteCursor.java:255)
at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:187)
at android.database.CursorWrapper.moveToPosition(CursorWrapper.java:187)
at android.support.v4.widget.CursorAdapter.getItemId(CursorAdapter.java:226)
at android.widget.AbsListView.onSaveInstanceState(AbsListView.java:910)
at android.widget.ListView.onSaveInstanceState(ListView.java:3687)
at android.view.View.dispatchSaveInstanceState(View.java:6070)
at android.view.ViewGroup.dispatchFreezeSelfOnly(ViewGroup.java:1197)
at android.widget.AdapterView.dispatchSaveInstanceState(AdapterView.java:759)
at android.view.ViewGroup.dispatchSaveInstanceState(ViewGroup.java:1184)
at android.view.View.saveHierarchyState(View.java:6053)
at android.support.v4.app.FragmentManagerImpl.saveFragmentViewState(FragmentManager.java:1387)
at android.support.v4.app.FragmentManagerImpl.saveAllState(FragmentManager.java:1439)
at android.support.v4.app.FragmentActivity.onSaveInstanceState(FragmentActivity.java:468)
at android.app.Activity.performSaveInstanceState(Activity.java:1040)
at android.app.Instrumentation.callActivityOnSaveInstanceState(Instrumentation.java:1180)
at android.app.ActivityThread.performPauseActivity(ActivityThread.java:3420)
... 12 more
And for good measures, one that at least has a single line in my code:
android.database.StaleDataException: Access closed cursor
at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:217)
at android.database.AbstractWindowedCursor.getBlob(AbstractWindowedCursor.java:27)
at android.database.CursorWrapper.getBlob(CursorWrapper.java:143)
at android.database.CursorWrapper.getBlob(CursorWrapper.java:143)
at com.testapp.TestFragment$1.setViewValue(TestFragment.java:84)
at android.support.v4.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:131)
at android.support.v4.widget.CursorAdapter.getView(CursorAdapter.java:257)
at android.widget.AbsListView.obtainView(AbsListView.java:1319)
at android.widget.ListView.makeAndAddView(ListView.java:1789)
at android.widget.ListView.fillDown(ListView.java:656)
at android.widget.ListView.fillSpecific(ListView.java:1342)
at android.widget.ListView.layoutChildren(ListView.java:1616)
at android.widget.AbsListView.onLayout(AbsListView.java:1172)
at android.view.View.layout(View.java:7037)
at android.widget.FrameLayout.onLayout(FrameLayout.java:333)
at android.view.View.layout(View.java:7037)
at android.widget.FrameLayout.onLayout(FrameLayout.java:333)
at android.view.View.layout(View.java:7037)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1249)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1125)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1042)
at android.view.View.layout(View.java:7037)
at android.widget.FrameLayout.onLayout(FrameLayout.java:333)
at android.view.View.layout(View.java:7037)
at android.widget.FrameLayout.onLayout(FrameLayout.java:333)
at android.view.View.layout(View.java:7037)
at android.view.ViewRoot.performTraversals(ViewRoot.java:1054)
at android.view.ViewRoot.handleMessage(ViewRoot.java:1736)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:143)
at android.app.ActivityThread.main(ActivityThread.java:4717)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
at dalvik.system.NativeStart.main(Native Method)
Update: Fixed, see my answer below
I have no cue what the problem is, but maybe this helps you pinning it down.
Create a LoggedCursor
like this and setup cursor factory so your query gives you the LoggedCursor:
class LoggedCursor extends SQLiteCursor {
@Override
public void close() {
Log.d(TAG, "Cursor closed by:", new RuntimeException("Stack trace"));
super.close();
}
}
The RuntimeException
is created just to log the stack trace easily, not for throwing it. When the cursor is closed you'll see the trace in logs.
Hopefully, this helps figuring out when and by whom is it closed.
I added logging as per JBM's suggestion and found the following:
I was using a subclass of CursorWrapper in my code. On the Droid 2, someone added a finalizer to CursorWrapper which closes the underlying Cursor. That meant that whenever our wrapper was garbage collected, the Cursor was closed. This finalizer does not exist on other phones or in the Android source code. Guess someone thought they were being smart?
Fixed by overriding the finalizer in our CursorWrapper
protected void finalize() throws Throwable {
// Do not remove this empty method. It is designed to prevent calls to super.
// Fixes bug on Droid 2, where CursorWrapper finalizer closes the Cursor!
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With