Greforb
Greforb

Reputation: 45

IllegalStateException: attempt to re-open an already-closed object. SimpleCursorAdapter problems

I'm totally new to android programming. I can see that this problem has been raised many times before. However, I am still not able to see what the problem is. I'm trying to connect the data from an SQLite database to a listview. In the ListActivity my onCreate looks like the following:

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_init_key);
    getActionBar().setDisplayHomeAsUpEnabled(true); 

    DBHandler db = new DBHandler(this);

    Cursor cursor = db.getKeyPointCursor(1, "Crataegus");
    // the desired columns to be bound
    String[] columns = new String[] { "question1","answer1" };
    // the XML defined views which the data will be bound to
    int[] to = new int[] { R.id.question, R.id.answer };

    SimpleCursorAdapter mAdapter = new SimpleCursorAdapter(this, R.layout.key_list_entry, cursor, columns, to, 0);

    cursor.close();
    this.setListAdapter(mAdapter);

}

It raises the following exception, which I hope somebody can help me with.

09-10 21:52:01.505: W/dalvikvm(10976): threadid=1: thread exiting with uncaught exception (group=0x40c8e1f8)
09-10 21:52:01.510: E/AndroidRuntime(10976): FATAL EXCEPTION: main
09-10 21:52:01.510: E/AndroidRuntime(10976): java.lang.RuntimeException: Unable to start activity ComponentInfo{jem.danskflora/jem.danskflora.InitKeyActivity}: java.lang.IllegalStateException: attempt to re-open an already-closed object: android.database.sqlite.SQLiteQuery (mSql = SELECT * FROM Crataegus WHERE _id=?) 
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1970)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1995)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.app.ActivityThread.access$600(ActivityThread.java:128)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1161)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.os.Handler.dispatchMessage(Handler.java:99)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.os.Looper.loop(Looper.java:137)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.app.ActivityThread.main(ActivityThread.java:4514)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at java.lang.reflect.Method.invokeNative(Native Method)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at java.lang.reflect.Method.invoke(Method.java:511)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:993)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:760)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at dalvik.system.NativeStart.main(Native Method)
09-10 21:52:01.510: E/AndroidRuntime(10976): Caused by: java.lang.IllegalStateException: attempt to re-open an already-closed object: android.database.sqlite.SQLiteQuery (mSql = SELECT * FROM Crataegus WHERE _id=?) 
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:33)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:82)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:164)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:156)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.widget.CursorAdapter.getCount(CursorAdapter.java:196)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.widget.ListView.setAdapter(ListView.java:467)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.app.ListActivity.setListAdapter(ListActivity.java:265)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at jem.danskflora.InitKeyActivity.onCreate(InitKeyActivity.java:30)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.app.Activity.performCreate(Activity.java:4562)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1053)
09-10 21:52:01.510: E/AndroidRuntime(10976):    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1934)
09-10 21:52:01.510: E/AndroidRuntime(10976):    ... 11 more

Upvotes: 3

Views: 7529

Answers (3)

Romain Guidoux
Romain Guidoux

Reputation: 2971

Do not close your cursor in your onCreate method. It is referenced by your adapter, but it is not already used !

Try using a LoaderManager / CursorLoader. It is the new way for handling cursors : How to transition from managedQuery to LoaderManager/CursorLoader?

Here is an example from one of my projects (I did not test this code):

public class AdditionalActivitiesListFragment extends ListActivity implements LoaderManager.LoaderCallbacks<Cursor>, OnItemClickListener, OnClickListener
{
    private SimpleCursorAdapter adapter;


    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        String[] columns = new String[] { "your", "db", "columns" };
        int[] to = new int[] { R.id.your, R.id.fields, R.id.toMapWith };

        getLoaderManager().initLoader(0x02, null, this);

        adapter = new SimpleCursorAdapter(activity.getApplicationContext(), R.layout.row_layout, null, columns, to, SimpleCursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
        setListAdapter(adapter);
    }

    public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1)
    {
        return new SimpleCursorLoader(this) {
            @Override
            public Cursor loadInBackground() {
                Cursor c = // Your cursor

                return c;
            }
        };
    }

    public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor)
    {
        adapter.swapCursor(cursor);
    }

    public void onLoaderReset(Loader<Cursor> arg0)
    {
        adapter.swapCursor(null);
    }
}

Source of the SimpleCursorLoader : https://gist.github.com/1217628 (via https://stackoverflow.com/a/7422343/615882 )

Upvotes: 4

soren.qvist
soren.qvist

Reputation: 7416

Your SimpleCursorAdapter is tied to your Cursor, it's 'supplying' your adapter with the data fromt the Cursor. Think of your adapter as just that, an adapter, it lies on top of your Cursor and handles the delegation of data from your Cursor to your ListView. So, you should only close your Cursor when you aren't using your ListView anymore, fx. in onPause() (when the Activity isn't shown to the user anymore).

Upvotes: 1

Jon O
Jon O

Reputation: 6591

I think the cursor continues to be used by the adapter, so you probably shouldn't close it right there.

You could use Activity.startManagingCursor() which will have the Activity take care of closing it for you when it's no longer used.

Upvotes: 0

Related Questions