Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android Compatibility Library CursorLoader : java.lang.IllegalStateException: Cursor is closed

Tags:

android

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

like image 712
Christopher Souvey Avatar asked Jul 01 '11 19:07

Christopher Souvey


2 Answers

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.

like image 150
JBM Avatar answered Nov 08 '22 18:11

JBM


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!
}
like image 37
Christopher Souvey Avatar answered Nov 08 '22 18:11

Christopher Souvey