title: Cursorloader not refreshing when underlying data changes
body: <p>I have a content provider, a content resolver, and a cursor loader. The loader is used to indirectly populate a listview (ie not a simple cursor adapter, rather an array adapter, since I need to use the cursor's results to gather other data).</p>
  
  <p>When I change the underlying data, the listview does not re-populate as the <code>onLoadFinished(Loader&lt;Cursor&gt;, Cursor)</code> call back is not called.</p>
  
  <p>As suggested while I'm writing this, there are a lot of questions on this issue.
  eg
  <a href="https://stackoverflow.com/questions/7915050/cursorloader-not-updating-after-data-change">CursorLoader not updating after data change</a></p>
  
  <p>And all the questions point out two things:</p>
  
  <ul>
  <li>In your content provider query() method, you need to tell the cursor about notifications a'la </li>
  </ul>
  
  <p><code>c.setNotificationUri(getContext().getContentResolver(), uri);</code></p>
  
  <ul>
  <li>In your content provider insert/update/delete method, you need to notify on the URI: </li>
  </ul>
  
  <p><code>getContext().getContentResolver().notifyChange(uri, null);</code></p>
  
  <p>I'm doing those things.</p>
  
  <p>I'm also NOT closing any of the cursors I get back.</p>
  
  <p>Note, my content provider lives in a separate app (think of it as a content provider app -- no launcher main activity). A com.example.provider APK, and the com.example.app is calling (in the content resolver) via the content://com.example.provider/table URI etc. The content resolver (dbhelper) lives in a library project (com.example.appdb) that the activity links in.  (This way, multiple projects can use the dbhelper via linking, and all content providers are installed via single APK)</p>
  
  <p>I have turned on debugging in the loader manager, and can see where I force a refresh after the data changes (ie the loader being restarted and previous being marked inactive), but nothing that says anything is happening automatically -- rather, in response to my force refresh.</p>
  
  <p>Any ideas why my loader isn't being refreshed ? </p>
  
  <p>-- EDIT -- </p>
  
  <p>The loader create:</p>
  
  <pre><code>Log.d(TAG, "onCreateLoader ID: " + loaderID);
  // typically a switch on the loader ID, and then 
  return dbHelper.getfoo()
  </code></pre>
  
  <p>Where getfoo() returns a cursor loader via:</p>
  
  <p><code>return new CursorLoader(context, FOO_URI, foo_Fields, foo_query, foo_argArray, foo_sort );</code></p>
  
  <p>Loader Finish takes the cursor and populates a table (and does some processing) -- nothing fancy there. </p>
  
  <pre><code>public void onLoadFinished(Loader&lt;Cursor&gt; loader, Cursor cursor) {
      Log.d(TAG, "onLoadFinished id: " + loader.getId());
      // switch on loader ID .. 
      FillTable(cursor, (FooAdapter) foo_info.listview.getAdapter();
  </code></pre>
  
  <p>Loader Reset clears the table.</p>
  
  <pre><code>public void onLoaderReset(Loader&lt;Cursor&gt; loader) {
      Log.d(TAG, "onLoaderReset id: " + loader.getId());
      // normally a switch on loader ID
      ((FooAdapter)foo_info.listview.getAdapter()).clear();
  </code></pre>
  
  <p>The content provider does:</p>
  
  <pre><code>public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
      Cursor cursor;
      SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
  
          SQLiteDatabase db = dbHelper.getReadableDatabase();
  
          switch (sUriMatcher.match(uri)) {
          case FOO:
              qb.setTables(FOO);
              qb.setProjectionMap(FooProjectionMap);
              break; 
          default:
              throw new IllegalArgumentException("Unknown URI " + uri);
          }
  
          Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
  
          c.setNotificationUri(getContext().getContentResolver(), uri);
          return c;
  }
  </code></pre>
  
  <p>Then insert/update is similar:</p>
  
  <pre><code>public Uri insert(Uri uri, ContentValues initialValues) {
      ContentValues values;
      if (initialValues != null) {
          values = new ContentValues(initialValues);
      } else {
          values = new ContentValues();
      }
  
      String tableName;
      Uri contentUri;
  
      switch (sUriMatcher.match(uri)) {
      case FOO:
          tableName = FOOTABLE;
          contentUri = FOO_URI;
          break;
  
      default:
          throw new IllegalArgumentException("Unknown URI " + uri);
      }
  
      SQLiteDatabase db = dbHelper.getWritableDatabase();
      long rowId = db.insert(tableName, null, values);
      if (rowId &gt; 0) {
          Uri objUri = ContentUris.withAppendedId(contentUri, rowId);
          getContext().getContentResolver().notifyChange(objUri, null);
          return objUri;
      }
  
      throw new SQLException("Failed to insert row into " + uri);
  }
  </code></pre>