Время жизни курсора базы данных Android

Я пытался понять и решить эту проблему в течение последних трех дней. У меня есть несколько ListActivities, каждый из которых запрашивает одну и ту же базу данных. В каждом действии я закрываю и снова открываю соединение с БД в onPause() и onResume() соответственно. Я заполняю адаптеры списка курсорами, которые также закрываю и снова открываю:

@Override
public void onPause(){
    super.onPause();

    if(currentCursor != null){
        currentCursor.close();
        currentCursor = null;
    }

    if(mDbHelper != null){
        mDbHelper.close();
        mDbHelper = null;
    }
}

@Override
public void onResume(){
    super.onResume();
    if(mDbHelper == null){
        mDbHelper = new DbHelper(this);
        mDbHelper.open();
    }

    if(currentCursor == null){
        new LoadNewListTask().execute();
    }
}


private class LoadNewListTask extends AsyncTask<String, Void, String> {
    ProgressDialog dialog;

    @Override
    protected void onPreExecute() {
        dialog = new ProgressDialog(context);
        dialog.setMessage("Loading...");
        dialog.show();
    }
    @Override
    protected String doInBackground(String... arg0) {
        try{
            currentCursor = mDbHelper.fetchNewRows();
        }catch(Exception e){}
        return null;
    }
    @Override
    protected void onPostExecute(String arg){
        if(dialog != null && dialog.isShowing()){
            dialog.dismiss();
        }
        if(currentCursor != null && currentCursor.getCount()>0){
            String[] from = new String[]{DbHelper.KEY_ID, sortingColumn};
            int[] to = new int[]{R.id.ai_about_author_btn, R.id.ai_author_name};

            MyAlphabetizedAdapter notes = new MyAlphabetizedAdapter(this, R.layout.author_index_item, currentCursor, from, to, sortingColumn);
            setListAdapter(notes);
        }
    }
}

На первый взгляд, это работает хорошо, однако, когда я быстро переключаюсь между действиями, я иногда получаю раздражающие исключения курсора, говорящие следующее:

05-01 14:45:05.849: ERROR/AndroidRuntime(1145):
java.lang.IllegalStateException: attempt to acquire a reference on a
close SQLiteClosable
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):     at
android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:31)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):     at
android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:56)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):     at
android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:49)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):     at
android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:49)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):     at
android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1118)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):     at
android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1092)
05-01 14:45:05.849: ERROR/AndroidRuntime(1145):     at
apt.tutorial.Restaurant.getAll(Restaurant.java:14)

и еще один, говорящий это (это происходит МНОГО на устройствах 2.1):

09-03 11:30:19.713: INFO/dalvikvm(2541): Uncaught exception thrown by finalizer (will be discarded):
09-03 11:30:19.713: INFO/dalvikvm(2541): Ljava/lang/IllegalStateException;: Finalizing cursor android.database.sqlite.SQLiteCursor@45b99828 on null that has not been deactivated or closed
09-03 11:30:19.713: INFO/dalvikvm(2541):     at android.database.sqlite.SQLiteCursor.finalize(SQLiteCursor.java:596)
09-03 11:30:19.713: INFO/dalvikvm(2541):     at dalvik.system.NativeStart.run(Native Method)

Более того, один из моих ListActivities реализует AlphabetIndexer, где я должен передать курсор. Очень странная вещь происходит после того, как я выхожу из активности и возвращаюсь с помощью кнопки «Назад». Список заполняется (как и ожидалось), однако, когда я пытаюсь использовать быструю прокрутку, я получаю нулевое исключение для курсора, даже если я воссоздал его и повторно заполнил список onResume().

Для меня это похоже на то, что ОС не может правильно обрабатывать несколько курсоров, или, может быть, я что-то здесь упускаю?

Спасибо за вашу помощь.


person Marqs    schedule 03.09.2011    source источник


Ответы (2)


В общем, у меня были проблемы с действиями, которые пытались поддерживать несколько курсоров. Особенно при попытке использовать управляемый курсор, которого я теперь стараюсь избегать, если это возможно (плюс они устарели). Теперь я пытаюсь открыть курсор, заполнить массив объектов нужными мне данными, закрыть курсор, а затем использовать ArrayAdapter для заполнения ListView. Если вам ДЕЙСТВИТЕЛЬНО нужно поддерживать курсор в рабочем состоянии, и вам нужно это делать только в том случае, если другие действия в вашем стеке изменяют данные, используйте диспетчер курсоров или, что еще лучше, более новый CursorLoader. При этом не закрывайте курсор, так как ваша деятельность будет управлять жизненным циклом курсора за вас.

person SBerg413    schedule 03.09.2011
comment
Я использую ArrayAdpters с пользовательскими объектами в своем другом проекте, и все работает нормально. Использование курсора требует меньше ресурсов, однако я не знаю, что управлять ими всеми будет так сложно. - person Marqs; 03.09.2011
comment
Да, я был там (что очевидно, но это один из моих вопросов, оставшихся без ответа). У меня было действие, когда я пытался управлять 3 или 4 курсорами одновременно. Что, наконец, сработало, так это использование подхода ArrayAdapter со всеми курсорами, кроме 1, которые мне нужно поддерживать (данные изменятся). - person SBerg413; 03.09.2011
comment
Я не думаю, что использование массивов вместо курсора — хорошая идея с точки зрения памяти. - person Raffaele; 07.05.2012

Поскольку вы запускаете асинхронную задачу и быстро покидаете действие, это означает, что при возврате задания у вас будет выход из этого действия, поэтому onPause() был вызван, а курсор не закрыт. Вы можете попробовать отменить асинхронную задачу в файле onPause().

person Dimitris Makris    schedule 03.09.2011
comment
У вас есть хорошая мысль, однако я жду завершения AsyncTask f, а затем перехожу к следующему действию. - person Marqs; 03.09.2011
comment
Хм, а вы проверили, возвращает ли fetchNewRows() значение null? - person Dimitris Makris; 03.09.2011
comment
да, я сделал, и он возвращает данные. Все работает, как и ожидалось, когда я не закрываю курсор onPause(), но мы должны, не так ли? Заботится ли SimpleCursorAdapter о времени жизни курсора? - person Marqs; 03.09.2011
comment
Нет, адаптер просто использует предоставленный вами курсор на основе жизненного цикла активности. Обработка жизненного цикла курсора должна быть повреждена активностью. - person Dimitris Makris; 03.09.2011
comment
но как я могу заставить активность начать управлять курсором? Раньше я использовал startManagingCursor(), однако этот метод устарел. - person Marqs; 03.09.2011
comment
Нет, как я могу вызвать manageQuery() в базе данных provate, я думал, что это только для поставщиков контента - person Marqs; 03.09.2011