Android ListView с SimpleCursorAdapter — сбои при возобновлении

У меня есть ListView в AcitivityA, который заполняется с помощью пользовательского SimpleCursorAdapter под названием RecipeAdapter. Адаптер содержит данные из SQLite

Существует представление EditText в верхней части ListView, которое фильтрует представление списка, когда пользователь ищет рецепт. Когда пользователь щелкает элемент в отфильтрованном ListView, запускается ActivityB.

Это все работает отлично. Однако, когда пользователь нажимает кнопку «Назад», чтобы возобновить ActivityB, я получаю следующую ошибку.

java.lang.IllegalStateException:
trying to requery an already closed cursor  android.database.sqlite.SQLiteCursor@418af170

Мои попытки решить проблему:

  • Дублирование кода из метода onCreate() в метод onResume.
  • Добавление метода c.requery() к методу onResume()
  • Добавление метода db.close к методу onDestroy()

Может ли кто-нибудь помочь мне с моей проблемой?

Вот мой код:

В onCreate cursor заполняют ListView с помощью c.getCursor, а когда пользователь фильтрует ListView через EditText, используется c.getFilterCursor.

public class RecipeActivity extends SherlockListActivity {

private DBHelper db = null;
private Cursor c = null;
private RecipeAdapter adapter = null;
ListView listContent;   
private EditText filterText = null;

@SuppressWarnings("deprecation")
@Override
public void onCreate(Bundle savedInstanceState) {
    try {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.filter_list);

        filterText = (EditText) findViewById(R.id.search_box);
        filterText.addTextChangedListener(filterTextWatcher);

        ListView listContent = getListView();

        db = new DBHelper(this);
        db.createDataBase();
        db.openDataBase();

        c = db.getCursor();         

        adapter = new RecipeAdapter(c);

        listContent.setAdapter(adapter);

        adapter.setFilterQueryProvider(new FilterQueryProvider() {
            public Cursor runQuery(CharSequence constraint) {
                // Search for states whose names begin with the specified letters.
                c = db.getFilterCursor(constraint);
                return c;
            }
        });

        startManagingCursor(c);


    } catch (IOException e) {
        e.printStackTrace();
    }
}




    private TextWatcher filterTextWatcher = new TextWatcher() {

    public void afterTextChanged(Editable s) {
    }

    public void beforeTextChanged(CharSequence s, int start, int count,
            int after) {
    }

    public void onTextChanged(CharSequence s, int start, int before,
            int count) {

        adapter.getFilter().filter(s);


    }

};

Внутренний класс RecipeAdapter

class RecipeAdapter extends CursorAdapter {

    @SuppressWarnings("deprecation")
    public RecipeAdapter(Cursor c) {
        super(RecipeActivity.this, c);
    }

    public void bindView(View row, Context arg1, Cursor arg2) {
        RecipeHolder holder = (RecipeHolder) row.getTag();
        holder.populateFrom(c, db);

    }

    public View newView(Context arg0, Cursor arg1, ViewGroup arg2) {
        LayoutInflater inflater = getLayoutInflater();
        View row = inflater.inflate(R.layout.reciperow, arg2, false);
        RecipeHolder holder = new RecipeHolder(row);
        row.setTag(holder);

        return (row);
    }


static class RecipeHolder {
    public TextView id = null;
    private TextView name = null;
    private TextView desc = null;
    private TextView preptime = null;
    private TextView cooktime = null;
    private TextView serves = null;
    private TextView calories = null;
    private TextView fat = null;
    private TextView fav = null;

    RecipeHolder(View row) {
        id = (TextView) row.findViewById(R.id.id);
        name = (TextView) row.findViewById(R.id.recipe);
        desc = (TextView) row.findViewById(R.id.desc);
        preptime = (TextView) row.findViewById(R.id.preptime);
        cooktime = (TextView) row.findViewById(R.id.cooktime);
        serves = (TextView) row.findViewById(R.id.serving);
        calories = (TextView) row.findViewById(R.id.calories);
        fat = (TextView) row.findViewById(R.id.fat);
        fav = (TextView) row.findViewById(R.id.fav);
    }


    void populateFrom(Cursor c, DBHelper r) {
        id.setText(r.getId(c));
        name.setText(r.getRecipe(c));
        name.setTextColor(Color.parseColor("#CCf27c22"));
        desc.setText(r.getDesc(c));
        preptime.setText(r.getPrepTime(c) + ". ");
        cooktime.setText(r.getCookTime(c) + " mins");
        serves.setText(r.getServes(c));
        calories.setText(r.getCalories(c));
        fat.setText(r.getFat(c));
        fav.setText(r.getFav(c));

Код getCursor() и getFilterCursor() из класса DBHelper

public Cursor getCursor() {
    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
    queryBuilder.setTables(DATABASE_TABLE);

    String[] columns = new String[] { KEY_ROWID, RECIPE, DESC, PREPTIME,
            COOKTIME, SERVES, CALORIES, FAT, CATEGORY, FAV };

    Cursor myCursor = queryBuilder.query(myDataBase, columns, null, null,
            null, null, RECIPE + " ASC");

    return myCursor;
}




public Cursor getFilterCursor(CharSequence constraint) {
    SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
    queryBuilder.setTables(DATABASE_TABLE);

    String[] columns = new String[] { KEY_ROWID, RECIPE, DESC, PREPTIME,
            COOKTIME, SERVES, CALORIES, FAT, CATEGORY, FAV };

    if (constraint == null || constraint.length() == 0) {
        // Return the full list
        return queryBuilder.query(myDataBase, columns, null, null, null,
                null, RECIPE + " ASC");
    } else {
        String value = "%" + constraint.toString() + "%";

        return myDataBase.query(DATABASE_TABLE, columns, "RECIPE like ? ",
                new String[] { value }, null, null, null);
    }
}

ПОЛНЫЙ ЛОГКАТ

FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to resume activity {ttj.android.quorn/ttj.android.quorn.RecipeActivity}: 
java.lang.IllegalStateException: trying to requery an already closed cursor android.database.sqlite.SQLiteCursor@41954658
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2456)
at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2484)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1185)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4507)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalStateException: trying to requery an already closed cursor android.database.sqlite.SQLiteCursor@41954658
at android.app.Activity.performRestart(Activity.java:4508)
at android.app.Activity.performResume(Activity.java:4531)
at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2446)

person tiptopjat    schedule 10.08.2012    source источник
comment
Пожалуйста, опубликуйте все ошибки LogCat и укажите строку, в которой произошла последняя ошибка.   -  person Sam    schedule 10.08.2012
comment
@Sam Logcat теперь включен в вопрос.   -  person tiptopjat    schedule 10.08.2012
comment
Поскольку вы открываете свою базу данных в onCreate(), вы должны добавить db.close() в onDestroy(). Какой код для db.getCursor()?   -  person Sam    schedule 10.08.2012
comment
@ Сэм, я добавил db.close() к onDestroy(), и это не имеет значения. Делюсь кодом getCursor() сейчас.   -  person tiptopjat    schedule 10.08.2012
comment
Использование db.close() — это просто хорошая практика. Между прочим, это отличное использование newView() и bindView(), вы не просто слепо переопределяете getView(). Однако я не могу понять ваш LogCat...   -  person Sam    schedule 10.08.2012


Ответы (1)


На какой версии платформы Android вы используете свое приложение? Этот метод startManagingCursor устарел, начиная с Honeycomb. Вместо этого разработчикам предлагается использовать новый класс CursorLoader с LoaderManager, который также доступен на старых платформах через пакет совместимости с Android.

На самом деле у меня такая же проблема на Honeycomb. Однако я не следовал приведенным выше инструкциям, так как у меня не так много времени, чтобы переписать свой код. Итак, вот мое решение, надеюсь, оно поможет. Но если у вас есть время, вы должны переключиться на использование CursorLoader и LoaderManager, которые обеспечивают гораздо лучшую производительность.

@Override
protected void onResume() {
    super.onResume();
    //do the query again every time on resume
    Cursor c = mExpenseDb.queryCategories(mSettings.getCurrentAccount().getId());
    //mAdapter is a SimpleCursorAdapter, set its cursor to the new one 
    mAdapter.changeCursor(c);
}

@Override
protected void onPause() {
    super.onPause();
    //mAdapter is a SimpleCursorAdapter, invalidate its data and set it cursor to null on Activity pause
    mAdapter.notifyDataSetInvalidated();

    mAdapter.changeCursor(null);
}
person Jay    schedule 10.08.2012
comment
Я дам этому шанс, когда я вернусь домой сегодня вечером. Должен ли я вообще изменить свой onCreate()? - person tiptopjat; 10.08.2012
comment
Я получаю сообщение об ошибке android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 70. В моем списке 70 пунктов. У меня есть cursor.moveToFirst() в моем getCursor() в DBHelper.class, но возникает исключение остановки. - person tiptopjat; 10.08.2012
comment
@tiptopjat Извините, я был в командировке, поэтому у меня не было возможности ответить на ваш вопрос. Вот мое предложение. Выполните инициализацию dbHelper и cursorAdaptor в методе onCreate(), а также выполните запрос к базе данных и измените курсор в методе onResume(). Что касается CursorIndexOutOfBoundsException, этого не должно происходить, если вы заранее вызываете cursor.moveToFirst(). Можете ли вы поделиться обновленным кодом, если это возможно? благодаря. -Джей - person Jay; 15.08.2012