6

Einer der Vorteile, die ich bei der Verwendung von CursorLoaders und Loadermanagers dachte, war, dass Sie den Lebenszyklus des Cursors nicht manuell verwalten mussten. Also habe ich einen Loadermanager verwendet, um einen Adapter mit Hilfe des Support-Pakets an eine AutoCompleteTextView zu binden.IllegalStateException - Unterstützung von LoaderManager mit AutocompleteTextView

Es funktioniert recht gut, außer dass es zufällig einen Fehler "IllegalStateException - Versuch, ein bereits geschlossenes Objekt wieder zu öffnen" löst. Das soll doch nicht passieren, wenn wir den Loader Manager benutzen?

Hier ist der Code:

package com.bhagwad.tennis; 

import android.appwidget.AppWidgetManager; 
import android.content.Intent; 
import android.database.Cursor; 
import android.os.Bundle; 
import android.support.v4.app.FragmentActivity; 
import android.support.v4.app.LoaderManager.LoaderCallbacks; 
import android.support.v4.content.CursorLoader; 
import android.support.v4.content.Loader; 
import android.support.v4.widget.SimpleCursorAdapter; 
import android.support.v4.widget.SimpleCursorAdapter.CursorToStringConverter; 
import android.text.Editable; 
import android.text.TextWatcher; 
import android.view.View; 
import android.view.View.OnClickListener; 
import android.widget.AutoCompleteTextView; 
import android.widget.Button; 

import com.bhagwad.tennis.TennisSchedule.TennisScheduleColumns; 


public class WidgetConfiguration extends FragmentActivity implements OnClickListener, LoaderCallbacks<Cursor> { 

    Button mSaveWidget; 
    AutoCompleteTextView mPlayerName; 
    int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; 
    String mSelection =""; 
    SimpleCursorAdapter mAdapter; 

    public static String PREFS = "com.bhagwad.tennis.appwidget"; 
    public static final String PREFS_PREFIX_KEY = "prefix_"; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.widget_configuration); 

     mPlayerName = (AutoCompleteTextView) findViewById(R.id.edit_filter); 

     mPlayerName.addTextChangedListener(new TextWatcher() { 

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

       if (!s.equals("")) 
        mSelection = s.toString(); 
       else 
        mSelection = ""; 

       getSupportLoaderManager().restartLoader(0, null, WidgetConfiguration.this); 



      } 

      @Override 
      public void beforeTextChanged(CharSequence s, int start, int count, 
        int after) { 
       // TODO Auto-generated method stub 

      } 

      @Override 
      public void afterTextChanged(Editable s) { 
       // TODO Auto-generated method stub 

      } 
     }); 

     // Set up the adapter 

     mAdapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_1, null, new String[] {TennisScheduleColumns.PLAYER_NAME}, new int[] {android.R.id.text1}, 0); 
     mAdapter.setCursorToStringConverter(new CursorToStringConverter() { 

      @Override 
      public CharSequence convertToString(Cursor c) { 

       return c.getString(c.getColumnIndexOrThrow(TennisScheduleColumns.PLAYER_NAME)); 

      } 
     }); 

     mPlayerName.setAdapter(mAdapter); 

     getSupportLoaderManager().initLoader(0, null, this); 

    } 


    @Override 
    public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { 

     return new CursorLoader(this, TennisScheduleColumns.CONTENT_URI_PLAYERS, new String[] {TennisScheduleColumns._ID, TennisScheduleColumns.PLAYER_NAME}, TennisScheduleColumns.PLAYER_NAME + " LIKE ?", new String[] {"%"+mSelection+"%"}, null); 

    } 


    @Override 
    public void onLoaderReset(Loader<Cursor> arg0) { 
     mAdapter.swapCursor(null); 

    } 

    @Override 
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 
     mAdapter.swapCursor(data); 


    } 


} 

Hier ist der Fehler-Stack:

08-16 22:21:23.244: E/AndroidRuntime(25475): java.lang.IllegalStateException: attempt to re-open an already-closed object: android.database.sqlite.SQLiteQuery (mSql = SELECT _id, player_name FROM players WHERE (player_name LIKE ?)) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:33) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.database.sqlite.SQLiteQuery.fillWindow(SQLiteQuery.java:82) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:164) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.database.sqlite.SQLiteCursor.onMove(SQLiteCursor.java:147) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.database.AbstractCursor.moveToPosition(AbstractCursor.java:178) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.database.CursorWrapper.moveToPosition(CursorWrapper.java:162) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.support.v4.widget.CursorAdapter.getItem(CursorAdapter.java:213) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.widget.AutoCompleteTextView.buildImeCompletions(AutoCompleteTextView.java:1113) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.widget.AutoCompleteTextView.showDropDown(AutoCompleteTextView.java:1072) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.widget.AutoCompleteTextView.updateDropDownForFilter(AutoCompleteTextView.java:950) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.widget.AutoCompleteTextView.onFilterComplete(AutoCompleteTextView.java:932) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.widget.Filter$ResultsHandler.handleMessage(Filter.java:285) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.os.Handler.dispatchMessage(Handler.java:99) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.os.Looper.loop(Looper.java:137) 
08-16 22:21:23.244: E/AndroidRuntime(25475): at android.app.ActivityThread.main(ActivityThread.java:4507) 

Alle Ideen, was falsch sein könnte?

+1

Obwohl ich immer noch keine Lösung habe, habe ich nur AutoCompleteTextView erweitert, den Fehler in onFilterComplete in einer try/catch-Schleife um die "super" -Anweisung abgefangen, ignoriert und weitergemacht. Ich habe keine Ahnung, warum es passiert ist oder wie man damit umgeht. Problem gelöst. Unordentlich. Würde eine elegantere Lösung schätzen. –

+0

Ich mache etwas ähnliches hier !! http://stackoverflow.com/questions/12854336/autocompletedtextview-backed-by-cursorloader – toobsco42

+0

@BhagwadJalPark Ich habe dieses Problem gelöst, wie Sie haben. Die von Tony vorgeschlagene Lösung (Überprüfung auf geschlossenen Zustand in OnLoadFinished) funktionierte nicht für mich. Hier ist die Wrapper-Klasse, die ich für Ihren Einblick geschrieben habe: https://gist.github.com/esilverberg/5606551 – esilver

Antwort

7

OnLoadFinished scheint manchmal mit einem toten Cursor aufgerufen zu werden - wenn Sie einen Test für isClosed() auf den Cursor setzen, werden Sie feststellen, dass es in einem (einige große) Versuche geschlossen ist.

Leider ist der ‚Standard‘ Code zu setzen in OnLoadFinished sofort ist ein changeCursor() auf dem Adapter und, na ja, was folgt Chaos, stackdumps, Seuchen usw.

Meine Lösung ist nicht hübscher als Ihr Versuch/Fang. Ignorieren Sie den falschen OnLoadFinished und riskieren Sie, dass der Endbenutzer eine leere UI erhält.

+1

Danke für diesen Leckerbissen. Für diesen speziellen Fall habe ich loadermanager/cursorloaer vollständig gelöscht und eine andere Möglichkeit implementiert, dies mit setfilterqueryprovider zu tun. Aber Ihre Lösung scheint der beste Weg zu sein. Vielleicht können wir irgendwo einen Fehler melden? –

Verwandte Themen