Search code examples
javaandroidandroid-sqliteandroid-contentprovidersqliteopenhelper

cursor throwing null pointer at cursor.close(); when called using getContentResolver().query() method


I am trying to make an app in which I can store and view my daily lectures, using sqlite. Everything was working fine before i started using the content provider. Whenever I try to query from the database it returns a null cursor.

Moreover, I cant even add a new lecture to the database. It seems like my ContentProvider class is not working properly. But I have absolutely no idea of whats wrong in it. I have added the source code below.

I have been stuck here for quite long. So any help is highly appreciated!

StudentProvider.java

package com.example.utkarsh_pc.tester_app.data;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;


public class StudentProvider extends ContentProvider {

private  final String LOG_TAG = StudentProvider.class.getSimpleName();

/** URI matcher code for the content URI for the lectures table */
private static final int LECTURES = 100;

/** URI matcher code for the content URI for a single lecture in the lectures table */
private static final int LECTURES_ID = 101;

/**
 * UriMatcher object to match a content URI to a corresponding code.
 * The input passed into the constructor represents the code to return for the root URI.
 * It's common to use NO_MATCH as the input for this case.
 */
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

// Static initializer. This is run the first time anything is called from this class.
static {
    // The calls to addURI() go here, for all of the content URI patterns that the provider
    // should recognize. All paths added to the UriMatcher have a corresponding code to return
    // when a match is found.

    // The content URI of the form "content://com.example.utkarsh_pc.tester_app/lectures" will map to the
    // integer code {@link #LECTURES}. This URI is used to provide access to MULTIPLE rows
    // of the pets table.
    sUriMatcher.addURI(StudentContract.CONTENT_AUTHORITY, StudentContract.PATH_LECTURES, LECTURES);

    // The content URI of the form "content://com.example.utkarsh_pc.tester_app/lectures/#" will map to the
    // integer code {@link #LECTURES_ID}. This URI is used to provide access to ONE single row
    // of the lectures table.
    //
    // In this case, the "#" wildcard is used where "#" can be substituted for an integer.
    // For example, "content://com.example.utkarsh_pc.tester_app/lectures/3" matches, but
    // "content://com.example.utkarsh_pc.tester_app/lectures" (without a number at the end) doesn't match.
    sUriMatcher.addURI(StudentContract.CONTENT_AUTHORITY, StudentContract.PATH_LECTURES + "/#", LECTURES_ID);
}

/** Database helper object */
private StudentDbHelper mDbHelper;

@Override
public boolean onCreate() {
    mDbHelper = new StudentDbHelper(getContext());
    return true;
}

@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                    String sortOrder) {
    // Get readable database
    SQLiteDatabase database = mDbHelper.getReadableDatabase();

    // This cursor will hold the result of the query
    Cursor cursor;

    // Figure out if the URI matcher can match the URI to a specific code
    switch (sUriMatcher.match(uri)) {
        case LECTURES:
            // For the LECTURES code, query the lectures table directly with the given
            // projection, selection, selection arguments, and sort order. The cursor
            // could contain multiple rows of the lectures table.
            cursor = database.query(StudentContract.LectureEntry.TABLE_NAME, projection, selection, selectionArgs,
                    null, null, sortOrder);
            break;
        case LECTURES_ID:
            // For the LECTURES_ID code, extract out the ID from the URI.
            // For an example URI such as "content://com.example.utkarsh_pc.tester_app/lectures/3",
            // the selection will be "_id=?" and the selection argument will be a
            // String array containing the actual ID of 3 in this case.
            //
            // For every "?" in the selection, we need to have an element in the selection
            // arguments that will fill in the "?". Since we have 1 question mark in the
            // selection, we have 1 String in the selection arguments' String array.
            selection = StudentContract.LectureEntry._ID + "=?";
            selectionArgs = new String[]{String.valueOf(ContentUris.parseId(uri))};

            // This will perform a query on the lectures table where the _id equals 3 to return a
            // Cursor containing that row of the table.
            cursor = database.query(StudentContract.LectureEntry.TABLE_NAME, projection, selection, selectionArgs,
                    null, null, sortOrder);
            break;
        default:
            throw new IllegalArgumentException("Cannot query unknown URI " + uri);
    }

        return cursor;

}

@Override
public Uri insert(Uri uri, ContentValues contentValues) {
    final int match = sUriMatcher.match(uri);
    switch (match) {
        case LECTURES:
            return insertLecture(uri, contentValues);
        default:
            throw new IllegalArgumentException("Insertion is not supported for " + uri);
    }
}

/**
 * Insert a lecture into the database with the given content values. Return the new content URI
 * for that specific row in the database.
 */
private Uri insertLecture(Uri uri, ContentValues values) {
    // Check that the name is not null
    String name = values.getAsString(StudentContract.LectureEntry.COLUMN_LECTURE_NAME);
    if (name == null) {
        throw new IllegalArgumentException("Lecture requires a name");
    }

    // Check that the day is valid
    Integer day = values.getAsInteger(StudentContract.LectureEntry.COLUMN_LECTURE_DAY);
    if (day == null || !StudentContract.LectureEntry.isValidDay(day)) {
        throw new IllegalArgumentException("Lecture requires valid day");
    }

    // No need to check the start_time or end_time, any time is valid (including null).

    // Get writeable database
    SQLiteDatabase database = mDbHelper.getWritableDatabase();

    // Insert the new lecture with the given values
    long id = database.insert(StudentContract.LectureEntry.TABLE_NAME, null, values);
    if (id == -1) {
        return null;
    }

    // Return the new URI with the ID (of the newly inserted row) appended at the end
    return ContentUris.withAppendedId(uri, id);
}

@Override
public int update(Uri uri, ContentValues contentValues, String selection,
                  String[] selectionArgs) {
    final int match = sUriMatcher.match(uri);
    switch (match) {
        case LECTURES:
            return updateLecture(uri, contentValues, selection, selectionArgs);
        case LECTURES_ID:
            // For the LECTURES_ID code, extract out the ID from the URI,
            // so we know which row to update. Selection will be "_id=?" and selection
            // arguments will be a String array containing the actual ID.
            selection = StudentContract.LectureEntry._ID + "=?";
            selectionArgs = new String[] { String.valueOf(ContentUris.parseId(uri)) };
            return updateLecture(uri, contentValues, selection, selectionArgs);
        default:
            throw new IllegalArgumentException("Update is not supported for " + uri);
    }
}

/**
 * Update lectures in the database with the given content values. Apply the changes to the rows
 * specified in the selection and selection arguments (which could be 0 or 1 or more lectures).
 * Return the number of rows that were successfully updated.
 */
private int updateLecture(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    // If the {@link LectureEntry#COLUMN_LECTURE_NAME} key is present,
    // check that the name value is not null.
    if (values.containsKey(StudentContract.LectureEntry.COLUMN_LECTURE_NAME)) {
        String name = values.getAsString(StudentContract.LectureEntry.COLUMN_LECTURE_NAME);
        if (name == null) {
            throw new IllegalArgumentException("Lecture requires a name");
        }
    }

    // If the {@link LectureEntry#COLUMN_LECTURE_DAY} key is present,
    // check that the day value is valid.
    if (values.containsKey(StudentContract.LectureEntry.COLUMN_LECTURE_DAY)) {
        Integer day = values.getAsInteger(StudentContract.LectureEntry.COLUMN_LECTURE_DAY);
        if (day == null || !StudentContract.LectureEntry.isValidDay(day)) {
            throw new IllegalArgumentException("Lecture requires valid day");
        }
    }

    // If the {@link LectureEntry#COLUMN_LECTURE_DAY} key is present,
    // check that the day value is valid.
    if (values.containsKey(StudentContract.LectureEntry.COLUMN_LECTURE_DAY)) {
        Integer day = values.getAsInteger(StudentContract.LectureEntry.COLUMN_LECTURE_DAY);

    }

    // No need to check the start_time and end_time, any value is valid (including null).

    // If there are no values to update, then don't try to update the database
    if (values.size() == 0) {
        return 0;
    }

    // Otherwise, get writeable database to update the data
    SQLiteDatabase database = mDbHelper.getWritableDatabase();

    // Returns the number of database rows affected by the update statement
    return database.update(StudentContract.LectureEntry.TABLE_NAME, values, selection, selectionArgs);
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
    // Get writeable database
    SQLiteDatabase database = mDbHelper.getWritableDatabase();

    final int match = sUriMatcher.match(uri);
    switch (match) {
        case LECTURES:
            // Delete all rows that match the selection and selection args
            return database.delete(StudentContract.LectureEntry.TABLE_NAME, selection, selectionArgs);
        case LECTURES_ID:
            // Delete a single row given by the ID in the URI
            selection = StudentContract.LectureEntry._ID + "=?";
            selectionArgs = new String[] { String.valueOf(ContentUris.parseId(uri)) };
            return database.delete(StudentContract.LectureEntry.TABLE_NAME, selection, selectionArgs);
        default:
            throw new IllegalArgumentException("Deletion is not supported for " + uri);
    }
}

@Override
public String getType(Uri uri) {
    final int match = sUriMatcher.match(uri);
    switch (match) {
        case LECTURES:
            return StudentContract.LectureEntry.CONTENT_LIST_TYPE;
        case LECTURES_ID:
            return StudentContract.LectureEntry.CONTENT_ITEM_TYPE;
        default:
            throw new IllegalStateException("Unknown URI " + uri + " with match " + match);
    }
}
}

StudentDbHelper.java

package com.example.utkarsh_pc.tester_app.data;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;


public class StudentDbHelper extends SQLiteOpenHelper {

public static final String LOG_TAG = StudentDbHelper.class.getSimpleName();

private static final String DATABASE_NAME = "student.db";

private static final int DATABASE_VERSION = 1;

public StudentDbHelper(Context context){

    super(context, DATABASE_NAME, null, DATABASE_VERSION);

}

@Override
public void onCreate(SQLiteDatabase db){

    String SQL_CREATE_TABLE = "CREATE TABLE " + StudentContract.LectureEntry.TABLE_NAME + " ("
            + StudentContract.LectureEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
            + StudentContract.LectureEntry.COLUMN_LECTURE_NAME + " TEXT NOT NULL, "
            + StudentContract.LectureEntry.COLUMN_LECTURE_TEACHER + " TEXT, "
            + StudentContract.LectureEntry.COLUMN_LECTURE_STARTTIME + " TEXT, "
            + StudentContract.LectureEntry.COLUMN_LECTURE_ENDTIME + " TEXT, "
            + StudentContract.LectureEntry.COLUMN_LECTURE_DAY + " INTEGER NOT NULL);";

    db.execSQL(SQL_CREATE_TABLE);

}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){}

}

StudentContract.java

package com.example.utkarsh_pc.tester_app.data;

import android.content.ContentResolver;
import android.net.Uri;
import android.provider.BaseColumns;

public final class StudentContract {

private StudentContract() {
}

public static final String CONTENT_AUTHORITY = "com.example.utkarsh_pc.tester_app";

public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);

public static final String PATH_LECTURES = "lectures";

public static final class LectureEntry implements BaseColumns {

    public static final Uri CONTENT_URI = Uri.withAppendedPath(BASE_CONTENT_URI, PATH_LECTURES);

    public static final String CONTENT_LIST_TYPE =
            ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_LECTURES;

    public static final String CONTENT_ITEM_TYPE =
            ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_LECTURES;

    public static final String TABLE_NAME = "lectures";

    public static final String _ID = BaseColumns._ID;

    public static final String COLUMN_LECTURE_NAME = "name";

    public static final String COLUMN_LECTURE_TEACHER = "teacher";

    public static final String COLUMN_LECTURE_STARTTIME = "start_time";

    public static final String COLUMN_LECTURE_ENDTIME = "end_time";

    public static final String COLUMN_LECTURE_DAY = "day";

    /**
     * Possible values for the day of the lecture.
     */
    public static final int DAY_MONDAY = 0;
    public static final int DAY_TUESDAY = 1;
    public static final int DAY_WEDNESDAY = 2;
    public static final int DAY_THURSDAY = 3;
    public static final int DAY_FRIDAY = 4;
    public static final int DAY_SATURDAY = 5;
    public static final int DAY_SUNDAY = 6;


    /**
     * Returns whether or not the given day is {@link #DAY_MONDAY}, {@link #DAY_TUESDAY},
     * {@link #DAY_WEDNESDAY},{@link #DAY_THURSDAY},{@link #DAY_FRIDAY},{@link #DAY_SATURDAY}
     * or {@link #DAY_SUNDAY}.
     */
    public static boolean isValidDay(int day) {
        if (day == DAY_MONDAY || day == DAY_TUESDAY || day == DAY_WEDNESDAY || day == DAY_THURSDAY || day == DAY_FRIDAY || day == DAY_SATURDAY || day == DAY_SUNDAY) {
            return true;
        } else {
            return false;
        }

    }
}
}

MondayActivity.java

package com.example.utkarsh_pc.tester_app;

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

import com.example.utkarsh_pc.tester_app.data.StudentContract;
import com.example.utkarsh_pc.tester_app.data.StudentDbHelper;
import com.example.utkarsh_pc.tester_app.data.StudentProvider;

import java.util.Objects;

public class MondayActivity extends AppCompatActivity {

ListView listView_mon;
String[] sub_mon = new String[]{"Sub1", "Sub2", "Sub3"};
FloatingActionButton fab;

private final String TAG = MondayActivity.class.getSimpleName();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_monday);

    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar1);
    setSupportActionBar(toolbar);
    getSupportActionBar().setTitle("Monday");

    fab = (FloatingActionButton) findViewById(R.id.fab1);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent(MondayActivity.this, AddSubject1Activity.class);
            startActivity(intent);
        }
    });
}

@Override
protected void onStart(){
    super.onStart();
    displayLectureInfo();
}

private void displayLectureInfo() {

    String[] projections = new String[]{StudentContract.LectureEntry._ID,
            StudentContract.LectureEntry.COLUMN_LECTURE_NAME,
            StudentContract.LectureEntry.COLUMN_LECTURE_STARTTIME,
            StudentContract.LectureEntry.COLUMN_LECTURE_ENDTIME,
            StudentContract.LectureEntry.COLUMN_LECTURE_TEACHER,
            StudentContract.LectureEntry.COLUMN_LECTURE_DAY};

    Cursor cursor;
    cursor = getContentResolver().query(
            StudentContract.LectureEntry.CONTENT_URI,   // The content URI of the words table
            projections,             // The columns to return for each row
            null,                   // Selection criteria
            null,                   // Selection criteria
            null);                  // The sort order for the returned rows

    TextView displayView = (TextView) findViewById(R.id.sample_view_1);


        try {
            displayView.setText("The lecture table contains " + cursor.getCount() + " lectures.\n\n");
            displayView.append(StudentContract.LectureEntry._ID + " - " +
                    StudentContract.LectureEntry.COLUMN_LECTURE_NAME + " - " +
                    StudentContract.LectureEntry.COLUMN_LECTURE_STARTTIME + " - " +
                    StudentContract.LectureEntry.COLUMN_LECTURE_ENDTIME + " - " +
                    StudentContract.LectureEntry.COLUMN_LECTURE_TEACHER + " - " +
                    StudentContract.LectureEntry.COLUMN_LECTURE_DAY + "\n");

            // Figure out the index of each column
            int idColumnIndex = cursor.getColumnIndex(StudentContract.LectureEntry._ID);
            int nameColumnIndex = cursor.getColumnIndex(StudentContract.LectureEntry.COLUMN_LECTURE_NAME);
            int starttimeColumnIndex = cursor.getColumnIndex(StudentContract.LectureEntry.COLUMN_LECTURE_STARTTIME);
            int endtimeColumnIndex = cursor.getColumnIndex(StudentContract.LectureEntry.COLUMN_LECTURE_ENDTIME);
            int teacherColumnIndex = cursor.getColumnIndex(StudentContract.LectureEntry.COLUMN_LECTURE_TEACHER);
            int dayColumnIndex = cursor.getColumnIndex(StudentContract.LectureEntry.COLUMN_LECTURE_DAY);

            // Iterate through all the returned rows in the cursor
            while (cursor.moveToNext() || cursor.isFirst()) {
                // Use that index to extract the String or Int value of the word
                // at the current row the cursor is on.
                int currentID = cursor.getInt(idColumnIndex);
                String currentName = cursor.getString(nameColumnIndex);
                String currentStartttime = cursor.getString(starttimeColumnIndex);
                String currentEndtime = cursor.getString(endtimeColumnIndex);
                String currentTeacher = cursor.getString(teacherColumnIndex);
                int currentDay = cursor.getInt(dayColumnIndex);
                // Display the values from each column of the current row in the cursor in the TextView
                displayView.append(("\n" + currentID + " - " +
                        currentName + " - " +
                        currentStartttime + " - " +
                        currentEndtime + " - " +
                        currentEndtime + " - " +
                        currentTeacher + " - " +
                        currentDay));
            }
        } finally {
            cursor.close();
        }
    }
@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.timetable_menu1, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
        case R.id.back:
            Intent intent = new Intent(MondayActivity.this, MainActivity.class);
            startActivity(intent);
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}
}

Solution

  • Did you add your ContentProvider in Manifest?

    <provider
            android:authorities="@string/content_authority"
            android:name=".package.ProviderClass"
            android:exported="false"
            android:syncable="true" />
    

    android:exported is false if only your app has access to ContentProvider