【问题标题】:IllegalStateException: attempt to re-open an already-closed object. SimpleCursorAdapter problemsIllegalStateException:尝试重新打开已经关闭的对象。 SimpleCursorAdapter 问题
【发布时间】:2012-09-10 20:23:13
【问题描述】:

我对 android 编程完全陌生。我可以看到这个问题之前已经提出过很多次了。但是,我仍然无法看到问题所在。我正在尝试将 SQLite 数据库中的数据连接到列表视图。在 ListActivity 中,我的 onCreate 如下所示:

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);

}

它引发了以下异常,我希望有人可以帮助我。

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

【问题讨论】:

    标签: android sqlite listview simplecursoradapter android-cursor


    【解决方案1】:

    不要在 onCreate 方法中关闭光标。它被您的适配器引用,但尚未使用!

    尝试使用 LoaderManager / CursorLoader。这是处理游标的新方法:How to transition from managedQuery to LoaderManager/CursorLoader?

    这是我的一个项目中的一个示例(我没有测试此代码):

    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);
        }
    }
    

    SimpleCursorLoader 的来源:https://gist.github.com/1217628(通过 https://stackoverflow.com/a/7422343/615882

    【讨论】:

    • 非常感谢您的回答!您使用的 SimpleCursorLoader 是这里建议的那个:stackoverflow.com/questions/7182485/…
    • 并且:在您的 OnLoadFinished 和 OnLoaderReset 中,您使用的适配器在 onCreate 中定义。你会怎么处理?抱歉问了,但有时 Java 不只是猜测我的想法真的让我很恼火 :)
    • @Greforb 哦,是的,对不起,我忘了精确定位 SimpleCursorLoader 的来源。是的,就是那个! :) 适配器在onCreate() 中定义,并且是您的活动的一个属性(在我的情况下,它是一个ListActivity,因此是setListAdapter() 调用)
    • 谢谢!很抱歉不清楚。适配器.swapCursor(光标);和 adapter.swapCursor(null);根据 Java 是不行的,因为适配器是在另一种方法中定义的。你能帮我吗?
    • @Greforb 这就是为什么它是你的 Activity 的一个属性,所以它在它的所有方法中都是可见的。我编辑我的答案
    【解决方案2】:

    您的 SimpleCursorAdapter 与您的 Cursor 相关联,它为您的适配器“提供”来自 Cursor 的数据。将您的适配器想象成一个适配器,它位于您的游标之上并处理从游标到您的 ListView 的数据委托。因此,只有在不再使用 ListView 时才应关闭光标,fx。在 onPause() 中(当 Activity 不再向用户显示时)。

    【讨论】:

      【解决方案3】:

      我认为adapter 继续使用cursor,因此您可能不应该在此处关闭它。

      您可以使用Activity.startManagingCursor(),当它不再使用时,Activity 会为您关闭它。

      【讨论】:

      • 但是这种方法已经被弃用了吧?据我所知,这实际上很奇怪,如果您使用 SQLite 数据库进行数据存储,则没有真正简单的替代方案,您必须采取各种解决方法。使用 LoaderManager/CursorLoader 你必须使用 ContentProvider,还是我完全错了?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-28
      • 1970-01-01
      • 2011-09-17
      • 1970-01-01
      相关资源
      最近更新 更多