Just like the title states When is Loader<Cursor> onCreateLoader(int, Bundle)
Called in Relation to Fragment Lifecycle? I am trying to implement a CursorLoader backed by a Content Provider to populate a ListView inside of a fragment. When the fragment is created the list view does not populate, but when I exit the activity (by launching another intent) and return to the fragment with the CursorLoader (causing Content Provider to be queried again) the list view is populated as expected.
This is leading me to suspect that my order of initialization and/or queries is incorrect. In my Content Provider query method I call cursor.setNotificationUri(contentResolver, uri)
and I call getContext().getContentResolver().notifyChange(uri, null)
inside of my insert statement.
Any help would be greatly appreciated!
ContentProvider
public class VenueProvider extends ContentProvider {
public static final String TAG = "VenueProvider";
DatabaseHelper databaseHelper;
public static final String AUTHORITY = "com.androidtitan.hotspots.Provider.VenueProvider";
public static final String BASE_PATH = DatabaseHelper.TABLE_VENUES;
public static String base_CONTENT_URI = "content://" + AUTHORITY
+ "/" + BASE_PATH + "/";
//MIME Types for getting a single item or a list of them
public static final String VENUES_MIME_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE
+ "/vnd.com.androidtitan.Data.venues";
public static final String VENUE_MIME_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE
+ "/vnd.com.androidtitan.Data.venues";
//Column names
public static class InterfaceConstants {
public static final String id = "_id";
public static final String venue_name = "venue_name";
public static final String venue_city = "venue_city";
public static final String venue_category = "venue_category";
public static final String venue_id_string = "venue_string";
public static final String venue_rating = "venue_rating";
}
//URI matcher variable
private static final int GET_ALL = 0;
private static final int GET_ONE = 1;
private static final int GET_SELECT = 2;
static UriMatcher uriMatcher = BuildUriMatcher();
static UriMatcher BuildUriMatcher() {
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
//Uri to match and the code to return when matched
matcher.addURI(AUTHORITY, BASE_PATH, GET_ALL);
matcher.addURI(AUTHORITY, BASE_PATH + "/#", GET_ONE);
matcher.addURI(AUTHORITY, BASE_PATH + "/*", GET_SELECT);
return matcher;
}
@Override
public boolean onCreate() {
databaseHelper = DatabaseHelper.getInstance(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Cursor queryCursor;
switch(uriMatcher.match(uri)) {
case GET_ALL:
queryCursor = databaseHelper.getReadableDatabase().rawQuery("SELECT * FROM "
+ DatabaseHelper.TABLE_VENUES, null);
break;
case GET_ONE:
queryCursor = databaseHelper.getReadableDatabase().rawQuery("SELECT * FROM "
+ DatabaseHelper.TABLE_VENUES
+ " WHERE " + DatabaseHelper.KEY_ID + " = " + uri.getLastPathSegment(), null);
break;
case GET_SELECT:
String selectionQuery = "SELECT * FROM " + DatabaseHelper.TABLE_VENUES + " td, "
+ DatabaseHelper.TABLE_COORDINATES + " tg, " + DatabaseHelper.TABLE_COORDINATES_VENUES
+ " tt WHERE tg." + DatabaseHelper.KEY_LOCAL + " = ? AND tg." + DatabaseHelper.KEY_ID
+ " = " + "tt." + DatabaseHelper.KEY_COORDS_ID + " AND td." + DatabaseHelper.KEY_ID
+ " = " + "tt." + DatabaseHelper.KEY_VENUES_ID;
Log.e(TAG, "selectionQuery: " + selectionQuery);
queryCursor = databaseHelper.getReadableDatabase().rawQuery(selectionQuery,
new String[] { String.valueOf(uri.getLastPathSegment()) });
break;
default:
throw new IllegalArgumentException("Unknown Uri:" + uri);
}
// Tell the cursor what uri to watch, so it knows when its source data changes
queryCursor.setNotificationUri(getContext().getContentResolver(), uri);
return queryCursor;
}
@Override
public String getType(Uri uri) {
switch(uriMatcher.match(uri)) {
case GET_ALL:
return VENUES_MIME_TYPE;
case GET_ONE:
return VENUE_MIME_TYPE;
case GET_SELECT:
return VENUES_MIME_TYPE;
default:
throw new IllegalArgumentException("Unknown uri: " + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
int uriType = uriMatcher.match(uri);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
long insertId = 0;
switch(uriType) {
case GET_ONE:
insertId = database.insert(DatabaseHelper.TABLE_VENUES, null, values);
break;
case GET_ALL:
break;
case GET_SELECT:
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
Log.e(TAG,"KEY_ID " + uri.getLastPathSegment()
+ " KEY_LOCATION_ID " + values.getAsLong(DatabaseHelper.KEY_VENUE_LOCATION_ID));
databaseHelper.assignVenueToLocation(Long.valueOf(uri.getLastPathSegment()),
values.getAsLong(DatabaseHelper.KEY_VENUE_LOCATION_ID));
getContext().getContentResolver().notifyChange(uri, null);
return uri;
//return Uri.parse(DatabaseHelper.TABLE_VENUES + "/" + insertId);
}
//todo:::
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int uriType = uriMatcher.match(uri);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
int updateId = 0;
switch(uriType) {
case GET_ONE:
updateId = database.update(DatabaseHelper.TABLE_VENUES, values, null, null);
break;
case GET_ALL:
break;
case GET_SELECT:
break;
default:
throw new IllegalArgumentException("Unknown URI: " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return updateId;
}
}
Fragment that initializes Loader
public class VenueResultsFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
private static final String TAG = "VenueResultsFragment";
DatabaseHelper databaseHelper;
LocationBundle focusLocation;
// String locationName;
int locationIndex;
private static final String[] PROJECTION = new String[]{
VenueProvider.InterfaceConstants.id,
VenueProvider.InterfaceConstants.venue_name,
VenueProvider.InterfaceConstants.venue_city,
VenueProvider.InterfaceConstants.venue_category,
VenueProvider.InterfaceConstants.venue_id_string,
VenueProvider.InterfaceConstants.venue_rating};
private static final int LOADER_ID = 1;
private LoaderManager.LoaderCallbacks<Cursor> callBacks;
private SimpleCursorAdapter adapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
databaseHelper = DatabaseHelper.getInstance(getActivity());
locationIndex = getArguments().getInt(MapsActivity.venueFragmentLocIndex);
focusLocation = databaseHelper.getAllLocations().get(locationIndex);
if (databaseHelper.getAllVenuesFromLocation(focusLocation).size() == 0) {
new FoursquareHandler(getActivity(), focusLocation.getLatlng().latitude,
focusLocation.getLatlng().longitude, locationIndex);
}
String[] dataColumns = {VenueProvider.InterfaceConstants.venue_name};
int[] viewItems = {R.id.nameTextView};
adapter = new SimpleCursorAdapter(getActivity(), R.layout.listview_venue_item, null,
dataColumns, viewItems, 0);
setListAdapter(adapter);
callBacks = this;
LoaderManager lm = getLoaderManager();
lm.initLoader(LOADER_ID, null, callBacks);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_venue_results, container, false);
return v;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
//mListener = (OnFragmentInteractionListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement Interface");
}
}
@Override
public void onDetach() {
super.onDetach();
//mListener = null;
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Log.e(TAG, "pre-cursorLoader locationName: " + focusLocation.getLocalName());
return new CursorLoader(getActivity(), Uri.parse(VenueProvider.base_CONTENT_URI + focusLocation.getLocalName()),
PROJECTION, null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
// switch (loader.getId()) {
//case LOADER_ID:
// break;
// }
adapter.swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
}