This was working! I'm not sure what changed. I do use git and I have looked over my commits and the code for hours now.
As the title indicates my uri matcher stopped matching.
Below I have pasted the relevant parts of my content provider.
public class Provider extends ContentProvider {
private static final String TAG = "Provider";
private static final String SCHEME = ContentResolver.SCHEME_CONTENT;
private static final String AUTHORITY = "com.snot.bodyweightworkout.database.provider";
private static final String BASE_URI = SCHEME + AUTHORITY;
public static final Uri URI_EXERCISES = Uri.parse(BASE_URI + "/exercise");
public static final Uri URI_PROGRAMS = Uri.parse(BASE_URI + "/program");
public static final Uri URI_PROGRAM_EXERCISES = Uri.parse(BASE_URI + "/program/#/exercise");
private static final int EXERCISE = 1;
private static final int EXERCISES = 2;
private static final int PROGRAM = 3;
private static final int PROGRAMS = 4;
private static final int PROGRAM_EXERCISES = 5;
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static
{
sURIMatcher.addURI(AUTHORITY, "/exercise", EXERCISES);
sURIMatcher.addURI(AUTHORITY, "/exercise/#", EXERCISE);
sURIMatcher.addURI(AUTHORITY, "/program", PROGRAMS);
sURIMatcher.addURI(AUTHORITY, "/program/#", PROGRAM);
sURIMatcher.addURI(AUTHORITY, "/program/#/exercise", PROGRAM_EXERCISES);
}
...
And then the part where the actual matching should take place.
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Log.v(TAG, "URI: " + uri);
Cursor result = null;
int match = sURIMatcher.match(uri);
switch(match)
{
case PROGRAMS:
result = DatabaseHandler
.getInstance(getContext())
.getReadableDatabase()
.query(Program.TABLE_NAME, Program.FIELDS, null, null, null, null, null, null);
result.setNotificationUri(getContext().getContentResolver(), URI_PROGRAMS);
break;
case PROGRAM:
final long pid = Long.parseLong(uri.getLastPathSegment());
result = DatabaseHandler
.getInstance(getContext())
.getReadableDatabase()
.query(Program.TABLE_NAME, Program.FIELDS,
Program.COL_ID + " IS ?",
new String[] { String.valueOf(pid) }, null, null, null, null);
result.setNotificationUri(getContext().getContentResolver(), URI_PROGRAMS);
break;
...
default:
throw new UnsupportedOperationException("Unmatched(" + match + ") URI: " + uri.toString());
I'm trying to query using a cursor loader like this:
getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(), Provider.URI_PROGRAMS, Program.FIELDS, null, null, null);
}
Everytime default is hit. Not match is made the I end up with a FC and the following line in my log.
E/AndroidRuntime( 1979): Caused by: java.lang.UnsupportedOperationException: Unmatched(-1) URI: content://com.snot.bodyweightworkout.database.provider/program
I've been starring at this for hours and I really need some fresh eyes to take a peak at it. So if some kind soul could take a look at it I will appreciate it very much. Thanks in advance.
There's no need for the starting /, so define your addUri() method calls like this:
static {
sURIMatcher.addURI(AUTHORITY, "exercise", EXERCISES);
sURIMatcher.addURI(AUTHORITY, "exercise/#", EXERCISE);
sURIMatcher.addURI(AUTHORITY, "program", PROGRAMS);
sURIMatcher.addURI(AUTHORITY, "program/#", PROGRAM);
sURIMatcher.addURI(AUTHORITY, "program/#/exercise", PROGRAM_EXERCISES);
}
Related
I have a Content Provider that can create one type of database. My database contains 5 columns and one table, and with my program, I want to be able to create a new table with the same columns as my first table. How can I change my Content Provider to do this ? I need to mach some uri, but how can I do this will I am in my program ?
My columns are :
_id, THEME, QUESTION, REPONSE, DIFFICULTE
Here is my Content Provider :
public class CardContentProvider extends ContentProvider {
private StockCard stock;
public static String authority = "com.example.jean.cartememoire.CardContentProvider";
private static String path ="Cartes_table";
public static final String _ID = "_id";
public static final String THEME = "THEME";
public static final String QUESTION = "QUESTION";
public static final String REPONSE = "REPONSE";
public static final String DIFFICULTE = "DIFFICULTE"; //# = un chiffre
public static final String STOCK_TABLE = "Cartes_table";
private static final int ID_STOCK_TABLE = 1;
private static final int ID_THEME = 2;
private static final int ID_QUESTION = 3;
private static final int ID_REPONSE = 4;
private static final int ID_DIFFICULTE = 5;
private static final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
matcher.addURI(authority, STOCK_TABLE, ID_STOCK_TABLE);
matcher.addURI(authority, STOCK_TABLE+"/*", ID_STOCK_TABLE);
}
#Override
public boolean onCreate() {
stock = StockCard.getInstance(getContext());
return true;
}
#Nullable
#Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = stock.getReadableDatabase();
int code = matcher.match(uri);
long id;
Cursor cursor;
switch (code)
{
case ID_STOCK_TABLE:
cursor = db.query(STOCK_TABLE, projection, selection,
selectionArgs, null, null, sortOrder);
break;
default:
Log.d("Uri provider =", uri.toString());
throw new UnsupportedOperationException("Pas encore implémenté");
}
return cursor;
}
#Nullable
#Override
public String getType(Uri uri) {
return null;
}
#Nullable
#Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = stock.getWritableDatabase();
int code = matcher.match(uri);
long id;
Uri.Builder builder = new Uri.Builder();
switch(code)
{
case ID_STOCK_TABLE:
System.out.println(values.toString());
id = db.insert(STOCK_TABLE, null, values);
builder.appendPath(STOCK_TABLE);
break;
default:
throw new UnsupportedOperationException("Pas encore implémenté");
}
builder.authority(authority);
builder = ContentUris.appendId(builder, id);
return builder.build();
}
#Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = stock.getWritableDatabase();
int code = matcher.match(uri);
int i;
switch(code)
{
case ID_STOCK_TABLE:
long id = ContentUris.parseId(uri);
i = db.delete(STOCK_TABLE, "_id=" + id, null);
break;
default:
throw new UnsupportedOperationException("Pas encore implémenté");
}
return 0;
}
#Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
return 0;
}
}
Thank you guys !
EDIT : I said rows instead of columns...
I have the next Content Provider
To insert a row in Table AGENDA, I do:
ContentValues values = new ContentValues(1);
values.put("MSG", "test");
context.getContentResolver().insert(DataProvider.CONTENT_URI_AGENDA, values);
and all works well.
But now, I´d like to use the uri with AGENDA_INSERTWITHCONFLICT to insert a row.
Please, How can I modify the line :
context.getContentResolver().insert(DataProvider.CONTENT_URI_AGENDA, values);
to do it?
Here is my Provider:
public class DataProvider extends ContentProvider {
public static final String TAGPROVIDER = "net.techabout.medappointment.provider";
public static final Uri CONTENT_URI_AGENDA = Uri.parse("content://"+TAGPROVIDER+"/agenda");
public static final String TABLE_AGENDA = "agenda";
private DbHelper dbHelper;
private static final int AGENDA_ALLROWS = 5;
private static final int AGENDA_INSERTWITHCONFLICT=7;
private static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(TAGPROVIDER, "agenda", AGENDA_ALLROWS);
uriMatcher.addURI(TAGPROVIDER, "agenda", AGENDA_INSERTWITHCONFLICT);
}
#Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
long id;
switch (uriMatcher.match(uri)) {
case AGENDA_ALLROWS:
id = db.insertOrThrow(TABLE_AGENDA, null, values);
break;
case AGENDA_INSERTWITHCONFLICT:
id=db.insertWithOnConflict(TABLE_AGENDA, BaseColumns._ID, values, SQLiteDatabase.CONFLICT_REPLACE);
break;
default:
throw new IllegalArgumentException("Unsupported URI: " + uri);
}
Uri insertUri = ContentUris.withAppendedId(uri, id);
getContext().getContentResolver().notifyChange(insertUri, null);
return insertUri;
}
}
make following changes, Please use naming convetions as required.
// content provider
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(TAGPROVIDER, "agenda", AGENDA_ALLROWS);
uriMatcher.addURI(TAGPROVIDER, "agenda_insert_conflicts", AGENDA_INSERTWITHCONFLICT);
}
calling mechanism
String URL = "net.techabout.medappointment.provider/agenda_insert_conflicts";
Uri uri = Uri.parse(URL);
context.getContentResolver().insert(uri , values);
This question already has answers here:
java.lang.NullPointerException: Attempt to invoke interface method 'boolean android.database.Cursor.moveToFirst()' on a null object reference [duplicate]
(1 answer)
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 2 years ago.
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.
Caused by: java.lang.NullPointerException: Attempt to invoke interface method 'int android.database.Cursor.getCount()' on a null object reference
at theo.testing.androidcustomloaders.fragments.MainActivityFragment.onActivityCreated(MainActivityFragment.java:74)
at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:2089)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1133)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1290)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:801)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1677)
at android.support.v4.app.FragmentController.execPendingActions(FragmentController.java:388)
at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:604)
at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:178)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1220)
at android.app.Activity.performStart(Activity.java:5992)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2261)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2358)
at android.app.ActivityThread.access$800(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1278)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5219)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:898)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693)
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...
I think you has a problem at using getActivity in onActivityCreated in your MainActivity Fragment
getActivity "might" return null if called from within onActivityCreated...especially during a configuration change like orientation change because the activity gets destroyed...
move that initialization to onAttach...
following link helped me to find out
getActivity return null in fragment onActivityCreated in some rooted device
Ok. I fixed it. All I did was changing my authority name from
theo.testing.customloaders
to
theo.testing.customloaders.app
I'm trying to add prepopulated SQLITE tables to my content provider using SQLiteAssetHelper but the uri matcher won't match. I can access/modify the tables through standard SQL but using a cursor loader throws an exception. Here is the relevant code in the content provider/cursor loader.
//Content provider code
private PantryDbHelper dbHelper;
private static final int PANTRY = 1;
private static final int INFO = 5;
public static final String AUTHORITY = "com.battlestarmathematica.stayfresh.pantryprovider";
//path to db
public static final String URL = "content://" + AUTHORITY;
public static final Uri CONTENT_URI = Uri.parse(URL);
public static final Uri CONTENT_URI_PANTRY = Uri.withAppendedPath(CONTENT_URI,"pantry");
public static final Uri CONTENT_URI_INFO = Uri.withAppendedPath(CONTENT_URI,"info");
static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY,"info",INFO);
uriMatcher.addURI(AUTHORITY, "pantry", PANTRY);
}
public Cursor query(#NonNull Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder){
SQLiteDatabase db = dbHelper.getReadableDatabase();
SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
switch (uriMatcher.match(uri)) {
case PANTRY:
builder.setTables(PantryContract.PANTRY_TABLE_NAME);
break;
case INFO:
builder.setTables("info");
default:
throw new IllegalArgumentException("Unsupported URI " + uri);
}
//Cursor loader code
public Loader<Cursor> onCreateLoader(int id, Bundle args){
return new CursorLoader(
//context
this,
//content URI
PantryContentProvider.CONTENT_URI_INFO,
//columns to return
new String[] {"_id","itemname"},
//selection
null,
//selection args
null,
//sort order
"itemname");
}
I know the cursor loader works because I use the exact same code for another activity with the pantry uri and it works perfectly. When I try to load it using the info uri though I get this exception.
java.lang.IllegalArgumentException: Unsupported URI content://com.battlestarmathematica.stayfresh.pantryprovider/info
Any help would be greatly appreciated.
You're just missing a break statement in your code.
The UriMatcher matches and the switch statement jumps to case INFO:, but since there is no break; the default: case is executed as well.
Try to replace this
case INFO:
builder.setTables("info");
default:
throw new IllegalArgumentException("Unsupported URI " + uri);
by this:
case INFO:
builder.setTables("info");
break;
default:
throw new IllegalArgumentException("Unsupported URI " + uri);
I have setup a fragment to pull data from a custom content provider using a CursorLoader.
The problem is that when i update a record in the SQLite table using the content resolver, the cursor does not refresh i.e. the getContext().getContentResolver().notifyChange(myUri, null) has no effect. I have to exit the fragment and open it again to see the change.
I think the problem is that the URI i have used to update a row is not being observed by the loader :
URI to create loader -content://com.myapp.provider/MyTable/Set/22
URI to update row -content://com.myapp.provider/MyTable/167
167 identifies a unique row in the table. 22 identifies a set of rows in the table. Is there some way to tell the loader that the row 167 comes within the set 22, so it should reset the cursor?
Here is the code in case it brings more clarity :
Creating CursorLoader in Fragment :
#Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle queryBundle) {
CursorLoader cursorLoader = new CursorLoader(getActivity(), Uri.parse("content://com.myapp.provider/MyTable/Set/22"), myProjection, null, null, null);
return cursorLoader;
}
on button click in fragment :
mContext.getContentResolver().update("content://com.myapp.provider/MyTable/167", values, null, null);
Content Provider class :
private static final String AUTHORITY = "com.myapp.provider";
private static final String TABLE_PATH = "MyTable";
public static final String CONTENT_URI_BASEPATH = "content://" + AUTHORITY + "/" + TABLE_PATH;
private static final int URITYPE_TABLE = 1;
private static final int URITYPE_SINGLE_SET = 2;
private static final int URITYPE_SINGLE_ROW = 3;
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static{
sUriMatcher.addURI(AUTHORITY, TABLE_PATH,URITYPE_TABLE);
sUriMatcher.addURI(AUTHORITY, TABLE_PATH + "/Set/#", URITYPE_SINGLE_SET);
sUriMatcher.addURI(AUTHORITY, TABLE_PATH + "/#", URITYPE_SINGLE_ROW);
}
#Override
public int update(Uri myUri, ContentValues values, String selection, String[] selectionArgs){
int rowCount = 0;
String id;
SQLiteDatabase db = localDB.getWritableDatabase();
int uriType = sUriMatcher.match(myUri);
switch(uriType){
case URITYPE_SINGLE_ROW :
id = uri.getLastPathSegment();
//selection and selectionArgs are ignored since the URI itself identifies a unique row.
rowCount = db.update(MyTable.TABLE_NAME, values, MyTable.COLUMN_ID + " = ?", new String[] {id});
}
getContext().getContentResolver().notifyChange(myUri, null);
return rowCount;
}
I has a similar issue and found the solution here.
In brief, it turned out I needed to call setNotificationUri(ContentResolver cr, Uri uri) on the cursor returned by the query() method of my content provider.
The solution is to call notifyChange() on the Uri that is being observed i.e. the set and not on the row.
To achieve this, we need to make some changes :
Include the set ID in the URI when calling the update :
mContext.getContentResolver().update("content://com.myapp.provider/MyTable/Set/22/167", values, null, null);
Change the URI pattern of a single row from "/#" to "/Set/#/#"
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static{
sUriMatcher.addURI(AUTHORITY, TABLE_PATH,URITYPE_TABLE);
sUriMatcher.addURI(AUTHORITY, TABLE_PATH + "/Set/#", URITYPE_SINGLE_SET);
sUriMatcher.addURI(AUTHORITY, TABLE_PATH + "/Set/#/#", URITYPE_SINGLE_ROW);
}
Then in the update function, construct a new Uri that has to be notified :
List<String> pathSegments = uri.getPathSegments();
String mySetID = pathSegments.get(2);
Uri mySetUri = Uri.parse("content://" + AUTHORITY + "/" + TABLE_PATH + "/Set/" + mySetID);
getContext().getContentResolver().notifyChange(mySetUri, null);