Java: ORMLite with SimpleCursorAdapter: StaleDataException

2k views Asked by At

This is my first time testing out SimpleCursorAdapter. I want to use it with ORMLite.

Unfortunately, I always get StaleDataException :(

EDIT: not sure anymore if adapter.swapCursor(c) is the problem. stepped over that without problems. the problem seems to be somewhere else /END EDIT

The error seems to occur in adapter.swapCursor(c). c is closed, this seems to be the problem, how can I fix that and why exactly is it closed? Note: the error ONLY occurs if the Einsatz1 sqllite table contains data. Everything runs perfectly if the table is empty.

This is my activity:

public class UebersichtEinsaetze extends FragmentActivity implements LoaderManager.LoaderCallbacks<Cursor>
{
...

  public DatabaseHelper helper = null;

  @Override
  protected void onDestroy()
  {
    super.onDestroy();
    ((SimpleCursorAdapter) einsatzListView.getAdapter()).getCursor().close();
    helper.close();
    if (helper != null)
    {
      OpenHelperManager.releaseHelper();
      helper = null;
    }

  }

  private DatabaseHelper getHelper()
  {
    if (helper == null)
    {
      helper = OpenHelperManager.getHelper(this, DatabaseHelper.class);
    }
    return helper;
  }

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

    setContentView(R.layout.activity_ubersicht_einsaetze);
    einsatzListView = (ListView) findViewById(android.R.id.list);
    einsatzDatumTextView = (TextView) findViewById(R.id.tv_uebersicht_abdatum);
    fillData();
    getHelper();
  }

  private void fillData()
  {
    String[] from = new String[] { "_id" };
    int[] to = new int[] { R.id.view_list_einsatz_text_projnr };

    adapter = new SimpleCursorAdapter(this, R.layout.view_list_eintrag_einsatz, null, from, to, 0);
    getSupportLoaderManager().initLoader(0, null, this);
    einsatzListView.setAdapter(adapter);
  }

  @Override
  public Loader<Cursor> onCreateLoader(int id, Bundle args)
  {
    String[] projection = { "_id" };
    CursorLoader cursorLoader = new CursorLoader(this, test.CONTENT_URI, projection, null, null, null);
    return cursorLoader;
  }

  @Override
  public void onLoadFinished(Loader<Cursor> loader, Cursor c)
  {
    adapter.swapCursor(c);
  }

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

This is my Contentprovider:

 private DatabaseHelper database;
  private RuntimeExceptionDao<Einsatz1, String> dao;

  // Used for the UriMacher
  private static final int EINSAETZE = 10;
  private static final int EINSATZ_ID = 20;

  private static final String AUTHORITY = "de.myapp.android";

  private static String BASE_PATH = "einsaetze";
  public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/" + BASE_PATH);

  public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/einsaetze";
  public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/einsatz";

  private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
  static
  {
    sURIMatcher.addURI(AUTHORITY, BASE_PATH, EINSAETZE);
    sURIMatcher.addURI(AUTHORITY, BASE_PATH + "/#", EINSATZ_ID);
  }

@Override
  public boolean onCreate()
  {
    database = new DatabaseHelper(getContext());
    this.dao = (RuntimeExceptionDao<Einsatz1, String>) database.getRTE(Einsatz1.class);
    return false;
  }

@Override
  public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
  {
    // ignore method parameters for now just to get it basically working

    QueryBuilder<Einsatz1, String> queryBuilder = this.dao.queryBuilder();

    Cursor cursor = null;
    CloseableIterator<Einsatz1> iterator = null;
    try
    {
      iterator = dao.iterator(queryBuilder.prepare());
      AndroidDatabaseResults results = (AndroidDatabaseResults) iterator.getRawResults();
      cursor = results.getRawCursor();
    }
    catch (SQLException e)
    {
      Log.e(getClass().getName(), "get cursor", e);
    }
    finally
    {
      iterator.closeQuietly();
    }

    // Make sure that potential listeners are getting notified
    cursor.setNotificationUri(getContext().getContentResolver(), uri);

    return cursor;
  }

Logcat:

01-09 10:01:29.591: E/AndroidRuntime(3767): FATAL EXCEPTION: main
01-09 10:01:29.591: E/AndroidRuntime(3767): android.database.StaleDataException: Attempting to access a closed CursorWindow.Most probable cause: cursor is deactivated prior to calling this method.
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.database.AbstractWindowedCursor.checkPosition(AbstractWindowedCursor.java:139)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.database.AbstractWindowedCursor.getString(AbstractWindowedCursor.java:50)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.database.CursorWrapper.getString(CursorWrapper.java:114)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.support.v4.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:135)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.support.v4.widget.CursorAdapter.getView(CursorAdapter.java:256)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.widget.AbsListView.obtainView(AbsListView.java:2267)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.widget.ListView.measureHeightOfChildren(ListView.java:1244)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.widget.ListView.onMeasure(ListView.java:1156)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.View.measure(View.java:15172)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.widget.RelativeLayout.measureChild(RelativeLayout.java:602)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.widget.RelativeLayout.onMeasure(RelativeLayout.java:415)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.View.measure(View.java:15172)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4814)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.View.measure(View.java:15172)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.widget.LinearLayout.measureVertical(LinearLayout.java:833)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.widget.LinearLayout.onMeasure(LinearLayout.java:574)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.View.measure(View.java:15172)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:4814)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at com.android.internal.policy.impl.PhoneWindow$DecorView.onMeasure(PhoneWindow.java:2148)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.View.measure(View.java:15172)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:1848)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1100)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1273)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:998)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4212)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:725)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.Choreographer.doCallbacks(Choreographer.java:555)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.Choreographer.doFrame(Choreographer.java:525)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:711)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.os.Handler.handleCallback(Handler.java:615)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.os.Handler.dispatchMessage(Handler.java:92)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.os.Looper.loop(Looper.java:137)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at android.app.ActivityThread.main(ActivityThread.java:4745)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at java.lang.reflect.Method.invokeNative(Native Method)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at java.lang.reflect.Method.invoke(Method.java:511)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
01-09 10:01:29.591: E/AndroidRuntime(3767):     at dalvik.system.NativeStart.main(Native Method)

Small note (unimportant, but if someone has info...): When I set breakpoint before adapter.swapCursor(c), it does not stop there before throwing the error. I have to set a breakpoint to do a dummy "int bla = 123" inserted before that statement. Similarly, setting a breakpoint anywhere in public Cursor query(...) method makes everything execute without errors (what the hell...), but again not stopping at the breakpoint.

2

There are 2 answers

0
jklp On

yep it looks like it's due to iterator.closeQuietly(), though the affect of commenting that out is you might now have a leak with many iterators opened (which should really be closed).

Instead, in your onQuery() method, try using the following which will give you a reference to the Cursor, which should close when your ContentProvider is closed.

Cursor cursor = getHelper().getReadableDatabase().query(tableName, projection, selection, selectionArgs, groupBy, having, sortOrder)
0
cdbeelala89 On

SOLVED

Comment out

iterator.closeQuietly();

Even though I found the code this came from here:

Android Cursor with ORMLite to use in CursorAdapter

...you should not rely on it.

I'll have to see what potential negative consequences the commenting out will have (if someone knows please tell me), but it everything seems to be working now.