I do have a problem that gives me a headache. I store some images of my city in the sqlite database via a custom content provider. However when I run my app I get a null cursor.
That would mean the the info are not stored correctly or the Uri of my provider is somehow faulty. So.
MyCityContract
public class MyCityContract { public static final String CONTENT_AUTHORITY = "theo.testing.customloaders"; public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY); public static final class MyCityEntry implements BaseColumns{ //table name public static final String TABLE_MY_CITY = "my_city"; //columns public static final String _ID = "_id"; public static final String COLUMN_NAME = "name"; public static final String COLUMN_ICON = "icon"; // create content uri public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon() .appendPath(TABLE_MY_CITY).build(); // create cursor of base type directory for multiple entries public static final String CONTENT_DIR_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + TABLE_MY_CITY; // create cursor of base type item for single entry public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE +"/" + CONTENT_AUTHORITY + "/" + TABLE_MY_CITY; // for building URIs on insertion public static Uri buildFlavorsUri(long id){ return ContentUris.withAppendedId(CONTENT_URI, id); } } }
MyCityDbHelper
public class MyCityDbHelper extends SQLiteOpenHelper{ public static final String LOG_TAG = MyCityDbHelper.class.getSimpleName(); //name & version public static final String DATABASE_NAME = "city.db"; public static final int DATABASE_VERSION = 4; // Create the database public MyCityDbHelper(Context context) { super(context, DATABASE_NAME,null,DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { final String SQL_CREATE_MY_CITY_TABLE = "CREATE TABLE " + MyCityContract.MyCityEntry.TABLE_MY_CITY + "(" + MyCityContract.MyCityEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + MyCityContract.MyCityEntry.COLUMN_NAME + " TEXT NOT NULL, " + MyCityContract.MyCityEntry.COLUMN_ICON + " INTEGER NOT NULL);"; sqLiteDatabase.execSQL(SQL_CREATE_MY_CITY_TABLE); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { Log.w(LOG_TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ". OLD DATA WILL BE DESTROYED"); // Drop the table sqLiteDatabase.execSQL("DROP TABLE IF EXISTS " + MyCityContract.MyCityEntry.TABLE_MY_CITY); sqLiteDatabase.execSQL("DELETE FROM SQLITE_SEQUENCE WHERE NAME = '" + MyCityContract.MyCityEntry.TABLE_MY_CITY + "'"); // re-create database onCreate(sqLiteDatabase); } }
MyCityProvider
public class MyCityProvider extends ContentProvider { private static final String LOG_TAG = MyCityProvider.class.getSimpleName(); private static final UriMatcher sUriMatcher = buildUriMatcher(); private MyCityDbHelper myCityDbHelper; //Codes for UriMatcher private static final int MY_CITY = 100; private static final int MY_CITY_WITH_ID = 200; private static UriMatcher buildUriMatcher(){ // Build a UriMatcher by adding a specific code to return based on a match // It's common to use NO_MATCH as the code for this case. final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); final String authority = MyCityContract.CONTENT_AUTHORITY; //add code for each URI matcher.addURI(authority,MyCityContract.MyCityEntry.TABLE_MY_CITY,MY_CITY); matcher.addURI(authority,MyCityContract.MyCityEntry.TABLE_MY_CITY + "/#",MY_CITY_WITH_ID); return matcher; } @Override public boolean onCreate() { myCityDbHelper = new MyCityDbHelper(getContext()); return true; } @Override public String getType(Uri uri) { final int match = sUriMatcher.match(uri); switch (match){ case MY_CITY: { return MyCityContract.MyCityEntry.CONTENT_DIR_TYPE; } case MY_CITY_WITH_ID:{ return MyCityContract.MyCityEntry.CONTENT_ITEM_TYPE; } default:{ throw new UnsupportedOperationException("Unknown uri: " + uri); } } } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder){ Cursor retCursor; switch(sUriMatcher.match(uri)){ // All Flavors selected case MY_CITY:{ retCursor = myCityDbHelper.getReadableDatabase().query( MyCityContract.MyCityEntry.TABLE_MY_CITY, projection, selection, selectionArgs, null, null, sortOrder); return retCursor; } // Individual flavor based on Id selected case MY_CITY_WITH_ID:{ retCursor = myCityDbHelper.getReadableDatabase().query( MyCityContract.MyCityEntry.TABLE_MY_CITY, projection, MyCityContract.MyCityEntry._ID + " = ?", new String[] {String.valueOf(ContentUris.parseId(uri))}, null, null, sortOrder); return retCursor; } default:{ // By default, we assume a bad URI throw new UnsupportedOperationException("Unknown uri: " + uri); } } } @Override public Uri insert(Uri uri, ContentValues contentValues) { final SQLiteDatabase db = myCityDbHelper.getWritableDatabase(); Uri returnUri; switch (sUriMatcher.match(uri)){ case MY_CITY: long _id = db.insert(MyCityContract.MyCityEntry.TABLE_MY_CITY,null,contentValues); Log.d("id",String.valueOf(_id)); // insert unless it is already contained in the database if(_id>0){ returnUri = MyCityContract.MyCityEntry.buildFlavorsUri(_id); }else { throw new android.database.SQLException("Failed to insert row into: " + uri); } break; default: { throw new UnsupportedOperationException("Unknown uri: " + uri ); } } getContext().getContentResolver().notifyChange(uri,null); return returnUri; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { final SQLiteDatabase db = myCityDbHelper.getWritableDatabase(); final int match = sUriMatcher.match(uri); int numDeleted; switch(match){ case MY_CITY: numDeleted = db.delete( MyCityContract.MyCityEntry.TABLE_MY_CITY, selection, selectionArgs); // reset _ID db.execSQL("DELETE FROM SQLITE_SEQUENCE WHERE NAME = '" + MyCityContract.MyCityEntry.TABLE_MY_CITY + "'"); break; case MY_CITY_WITH_ID: numDeleted = db.delete(MyCityContract.MyCityEntry.TABLE_MY_CITY, MyCityContract.MyCityEntry._ID + " = ?", new String[]{String.valueOf(ContentUris.parseId(uri))}); // reset _ID db.execSQL("DELETE FROM SQLITE_SEQUENCE WHERE NAME = '" + MyCityContract.MyCityEntry.TABLE_MY_CITY + "'"); break; default: throw new UnsupportedOperationException("Unknown uri: " + uri); } return numDeleted; } @Override public int bulkInsert(Uri uri, ContentValues[] values){ final SQLiteDatabase db = myCityDbHelper.getWritableDatabase(); final int match = sUriMatcher.match(uri); switch(match){ case MY_CITY: // allows for multiple transactions db.beginTransaction(); // keep track of successful inserts int numInserted = 0; try{ for(ContentValues value : values){ if (value == null){ throw new IllegalArgumentException("Cannot have null content values"); } long _id = -1; try{ _id = db.insertOrThrow(MyCityContract.MyCityEntry.TABLE_MY_CITY, null, value); }catch(SQLiteConstraintException e) { Log.w(LOG_TAG, "Attempting to insert " + value.getAsString( MyCityContract.MyCityEntry.COLUMN_NAME) + " but value is already in database."); } if (_id != -1){ numInserted++; } } if(numInserted > 0){ // If no errors, declare a successful transaction. // database will not populate if this is not called db.setTransactionSuccessful(); } } finally { // all transactions occur at once db.endTransaction(); } if (numInserted > 0){ // if there was successful insertion, notify the content resolver that there // was a change getContext().getContentResolver().notifyChange(uri, null); } return numInserted; default: return super.bulkInsert(uri, values); } } @Override public int update(Uri uri, ContentValues contentValues, String selection, String[] selectionArgs){ final SQLiteDatabase db = myCityDbHelper.getWritableDatabase(); int numUpdated = 0; if (contentValues == null){ throw new IllegalArgumentException("Cannot have null content values"); } switch(sUriMatcher.match(uri)){ case MY_CITY:{ numUpdated = db.update(MyCityContract.MyCityEntry.TABLE_MY_CITY, contentValues, selection, selectionArgs); break; } case MY_CITY_WITH_ID: { numUpdated = db.update(MyCityContract.MyCityEntry.TABLE_MY_CITY, contentValues, MyCityContract.MyCityEntry._ID + " = ?", new String[] {String.valueOf(ContentUris.parseId(uri))}); break; } default:{ throw new UnsupportedOperationException("Unknown uri: " + uri); } } if (numUpdated > 0){ getContext().getContentResolver().notifyChange(uri, null); } return numUpdated; } }
MainActivityFragment
public class MainActivityFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor>{ private static final String LOG_TAG = MainActivityFragment.class.getSimpleName(); private MyCityAdpapter myCityAdpapter; private static final int CURSOR_LOADER_ID = 0; private GridView mGridView; MyCity[] mMyCity = { new MyCity("Ancient Theatre - Larisa", R.drawable.larissa1), new MyCity("Ancient Theatre - Larisa", R.drawable.larissa2), new MyCity("Municipality park", R.drawable.larissa3), new MyCity("Municipality park", R.drawable.larissa4), new MyCity("Old trains",R.drawable.larissa5), new MyCity("Old trains",R.drawable.larissa6), new MyCity("Church", R.drawable.larissa7), new MyCity("Church", R.drawable.larissa8), new MyCity("Alcazar park", R.drawable.larissa9), new MyCity("Alcazar park", R.drawable.larissa10), new MyCity("AEL FC Arena", R.drawable.larissa11), new MyCity("AEL FC Arena", R.drawable.larissa12), new MyCity("Larissa Fair", R.drawable.larissa13), new MyCity("Larissa Fair", R.drawable.larissa14), new MyCity("Larissa Fair", R.drawable.larissa15), new MyCity("Larissa Fair", R.drawable.larissa16) }; public MainActivityFragment() { // Required empty public constructor } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { Cursor c = getActivity().getContentResolver().query(MyCityContract.MyCityEntry.CONTENT_URI, new String[]{MyCityContract.MyCityEntry._ID}, null, null, null); if (c.getCount() == 0){ insertData(); } // initialize loader getLoaderManager().initLoader(CURSOR_LOADER_ID, null, this); super.onActivityCreated(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment // inflate fragment_main layout final View rootView = inflater.inflate(R.layout.fragment_main_activity, container, false); // initialize our FlavorAdapter myCityAdpapter = new MyCityAdpapter(getActivity(), null, 0, CURSOR_LOADER_ID); // initialize mGridView to the GridView in fragment_main.xml mGridView = (GridView) rootView.findViewById(R.id.flavors_grid); // set mGridView adapter to our CursorAdapter mGridView.setAdapter(myCityAdpapter); return rootView; } // insert data into database public void insertData(){ ContentValues[] cityValuesArr = new ContentValues[mMyCity.length]; // Loop through static array of MyCity, add each to an instance of ContentValues // in the array of ContentValues for(int i = 0; i < mMyCity.length; i++){ cityValuesArr[i] = new ContentValues(); cityValuesArr[i].put(MyCityContract.MyCityEntry.COLUMN_ICON, mMyCity[i].image); cityValuesArr[i].put(MyCityContract.MyCityEntry.COLUMN_NAME, mMyCity[i].name); } // bulkInsert our ContentValues array getActivity().getContentResolver().bulkInsert(MyCityContract.MyCityEntry.CONTENT_URI, cityValuesArr); } @Override public Loader<Cursor> onCreateLoader(int id, Bundle args){ return new CursorLoader(getActivity(), MyCityContract.MyCityEntry.CONTENT_URI, null, null, null, null); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { myCityAdpapter.swapCursor(data); } @Override public void onLoaderReset(Loader<Cursor> loader){ myCityAdpapter.swapCursor(null); } }
I read the Cursor inside the onCreateView(...) method of my Fragment
@Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { Cursor c = getActivity().getContentResolver().query(MyCityContract.MyCityEntry.CONTENT_URI, new String[]{MyCityContract.MyCityEntry._ID}, null, null, null); if (c.getCount() == 0){ insertData(); } // initialize loader getLoaderManager().initLoader(CURSOR_LOADER_ID, null, this); super.onActivityCreated(savedInstanceState); }
and this is where I am thrown the null Cursor exception.
Any ideas?
Thanks,
Theo.
EDIT
I changed this if condition from
if (c.getCount() == 0){ insertData(); }
to
if (c == null){ insertData(); }
and I am getting this exception!
Caused by: java.lang.IllegalArgumentException: Unknown URL content://theo.testing.customloaders/my_city
So there is be an error with provider. hmm...