0

I have database problem in my Android app that I can't seem to figure out. I do not see any errors in my code but when I run the application, the app crashes with LogCat pointing to multiple points in my code. Among these are: NullPointerExceptions and SQLiteOpenHelper (on .getWritableDatabase).

There seems to be a problem with the code in the onCreate method of Notifications.java Class and another one in the open method in NotificationsDbAdapter.java class.

The code files and the LogCat error log are shown below.

How do I solve this? Any help will be highly appreciated?


1. Notifications.java

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

        mDbHelper = new NotificationsDbAdapter();
        mDbHelper.open();
        fillData();

        registerForContextMenu(getListView());
    }

    @SuppressWarnings("deprecation")
    private void fillData() {
        Cursor notificationsCursor = mDbHelper.fetchAllNotifications();
        startManagingCursor(notificationsCursor);
        // Create an array to specify the fields we want (only the TITLE)
        String[] from = new String[]{NotificationsDbAdapter.KEY_TITLE};
        // and an array of the fields we want to bind in the view
        int[] to = new int[]{R.id.notifytext};
        // Now create a simple cursor adapter and set it to display
        SimpleCursorAdapter notifications =
                new SimpleCursorAdapter(this, R.layout.notify_row, notificationsCursor, from, to);
        setListAdapter(notifications);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.notify_list, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.action_add_notify:
            createReminder();
            return true;
        case android.R.id.home:
            finish();
            break;

        default:
            break;
        }
        return super.onOptionsItemSelected(item);
    }

    private void createReminder() {
        Intent i = new Intent(this, NotificationEditor.class);
        startActivityForResult(i, ACTIVITY_CREATE);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        fillData();
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        Intent i = new Intent(this, NotificationEditor.class);
        i.putExtra(NotificationsDbAdapter.KEY_ROWID, id);
        startActivityForResult(i, ACTIVITY_EDIT);

    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,
            ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);

        MenuInflater mi = getMenuInflater();
        mi.inflate(R.menu.ctx_notify_delete, menu);
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.notify_delete:
            AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
            mDbHelper.deleteNotification(info.id);
            fillData();
            return true;
        }
        return super.onContextItemSelected(item);
    }

}
tring dateForButton = dateFormat.format(mCalendar.getTime());
        mDateButton.setText(dateForButton);
    }

    private void updateTimeButtonText() {
        SimpleDateFormat timeFormat = new SimpleDateFormat(TIME_FORMAT);
        String timeForButton = timeFormat.format(mCalendar.getTime());
        mTimeButton.setText(timeForButton);
    }

}

2. NotificationEditor.java

;

    private long mRowId;

    private Calendar mCalendar;
    private Button mDateButton;
    private Button mTimeButton;
    private EditText mTitleText;
    private EditText mDescText;
    private Button mSaveButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mDbHelper = new NotificationsDbAdapter();

        setContentView(R.layout.notification_editor);


        mCalendar = Calendar.getInstance();
        mDateButton = (Button) findViewById(R.id.notify_date);
        mTimeButton = (Button) findViewById(R.id.notify_time);
        mSaveButton = (Button) findViewById(R.id.notify_save);
        mTitleText = (EditText) findViewById(R.id.title);
        mDescText = (EditText) findViewById(R.id.descri);


        mRowId = savedInstanceState != null
                ? savedInstanceState.getLong(NotificationsDbAdapter.KEY_ROWID)
                        : null;

                registerButtonListenersAndSetDefaultText();

    }

    private void setRowIdFromIntent() {
        if (mRowId == 0L) {
            Bundle extras = getIntent().getExtras();
            mRowId = extras != null
                    ? extras.getLong(NotificationsDbAdapter.KEY_ROWID)
                            : null;
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        mDbHelper.open();
        setRowIdFromIntent();
        try {
            populateFields();
        } catch (java.text.ParseException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        mDbHelper.close();
    }

    private void registerButtonListenersAndSetDefaultText() {
        mDateButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                showDialog(DATE_PICKER_DIALOG);
            }
        });

        mTimeButton.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                showDialog(TIME_PICKER_DIALOG);
            }
        });

        updateDateButtonText();
        updateTimeButtonText();

        mSaveButton.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                saveState();
                setResult(RESULT_OK);
                Toast.makeText(NotificationEditor.this,
                        getString(R.string.txt_reminder_saved_message),
                        Toast.LENGTH_SHORT).show();
                finish();
            }

            private void saveState() {
                String title = mTitleText.getText().toString();
                String description = mDescText.getText().toString();
                SimpleDateFormat dateTimeFormat = new
                        SimpleDateFormat(DATE_TIME_FORMAT);
                String notificationDateTime = dateTimeFormat.format(mCalendar.getTime());

                if (mRowId == 0L) {
                    long id = mDbHelper.createNotification(title, description, notificationDateTime);
                    if (id > 0) {
                        mRowId = id;
                    }
                } else {
                    mDbHelper
                    .updateNotification(mRowId, title, description, notificationDateTime);
                }
            }
        });
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putLong(NotificationsDbAdapter.KEY_ROWID, mRowId);
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        switch(id) {
        case DATE_PICKER_DIALOG:
            return showDatePicker();

        case TIME_PICKER_DIALOG:
            return showTimePicker();
        }
        return super.onCreateDialog(id);
    }
    private DatePickerDialog showDatePicker() {
        DatePickerDialog datePicker = new DatePickerDialog(NotificationEditor.this,
                new DatePickerDialog.OnDateSetListener() {
            @Override
            public void onDateSet(DatePicker view, int year, int monthOfYear,
                    int dayOfMonth) {
                mCalendar.set(Calendar.YEAR, year);
                mCalendar.set(Calendar.MONTH, monthOfYear);
                mCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
                updateDateButtonText();
            }
        }, mCalendar.get(Calendar.YEAR), mCalendar.get(Calendar.MONTH),
        mCalendar.get(Calendar.DAY_OF_MONTH));
        return datePicker;
    }

    private TimePickerDialog showTimePicker() {
        TimePickerDialog timePicker = new TimePickerDialog(this, new
                TimePickerDialog.OnTimeSetListener() {
            @Override
            public void onTimeSet(TimePicker view, int hourOfDay, int minute){
                mCalendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
                mCalendar.set(Calendar.MINUTE, minute);
                updateTimeButtonText();
            }
        }, mCalendar.get(Calendar.HOUR_OF_DAY),
        mCalendar.get(Calendar.MINUTE), true);
        return timePicker;
    }

    private void populateFields() throws java.text.ParseException {
        if (mRowId != 0L) {
            Cursor notification = mDbHelper.fetchNotification(mRowId);
            startManagingCursor(notification);
            mTitleText.setText(notification.getString(
                    notification.getColumnIndexOrThrow(NotificationsDbAdapter.KEY_TITLE)));
            mDescText.setText(notification.getString(
                    notification.getColumnIndexOrThrow(NotificationsDbAdapter.KEY_DESC)));
            SimpleDateFormat dateTimeFormat =
                    new SimpleDateFormat(DATE_TIME_FORMAT);
            Date date = null;
            try {
                String dateString = notification.getString(
                        notification.getColumnIndexOrThrow(
                                NotificationsDbAdapter.KEY_DATE_TIME));
                date = dateTimeFormat.parse(dateString);
                mCalendar.setTime(date);
            } catch (ParseException e) {
                Log.e("NotificationEditor", e.getMessage(), e);
            }

        }

    }

    private void updateDateButtonText() {
        SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
        String dateForButton = dateFormat.format(mCalendar.getTime());
        mDateButton.setText(dateForButton);
    }

    private void updateTimeButtonText() {
        SimpleDateFormat timeFormat = new SimpleDateFormat(TIME_FORMAT);
        String timeForButton = timeFormat.format(mCalendar.getTime());
        mTimeButton.setText(timeForButton);
    }

}

3. NotificationsDbAdapter.java

public class NotificationsDbAdapter {



    private static final String DATABASE_NAME = "mpegdb";
    private static final String DATABASE_TABLE = "notifications";
    private static final int DATABASE_VERSION = 1;
    public static final String KEY_TITLE = "title";
    public static final String KEY_DESC = "description";
    public static final String KEY_DATE_TIME = "notification_date_time";
    public static final String KEY_ROWID = "_id";
    private DatabaseHelper mDbHelper;
    private SQLiteDatabase mDb;
    private static final String DATABASE_CREATE =
            "create table " + DATABASE_TABLE + " ("
                    + KEY_ROWID + " integer primary key autoincrement, "
                    + KEY_TITLE + " text not null, "
                    + KEY_DESC + " text not null, "
                    + KEY_DATE_TIME + " text not null);";
    private Context mCtx;
    public void notificationsDbAdapter(Context ctx) {
        this.mCtx = ctx;
    }
    public NotificationsDbAdapter open() throws android.database.SQLException {

        Log.i(LOGTAG, "Database opened");

        mDbHelper = new DatabaseHelper(mCtx);
        mDb = mDbHelper.getWritableDatabase();
        return this;
    }
    public void close() {

        Log.i(LOGTAG, "Database closed");

        mDbHelper.close();
    }
    public long createNotification(String title, String description, String
            notificationDateTime) {
        ContentValues initialValues = new ContentValues();
        initialValues.put(KEY_TITLE, title);
        initialValues.put(KEY_DESC, description);
        initialValues.put(KEY_DATE_TIME, notificationDateTime);
        return mDb.insert(DATABASE_TABLE, null, initialValues);
    }
    public boolean deleteNotification(long rowId) {
        return
                mDb.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0;
    }

    public Cursor fetchAllNotifications() {
        return mDb.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE,
                KEY_DESC, KEY_DATE_TIME}, null, null, null, null, null);
    }
    public Cursor fetchNotification(long rowId) throws SQLException {
        Cursor mCursor =
                mDb.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
                        KEY_TITLE, KEY_DESC, KEY_DATE_TIME}, KEY_ROWID + "=" +
                                rowId, null,
                                null, null, null, null);
        if (mCursor != null) {
            mCursor.moveToFirst();
        }
        return mCursor;
    }
    public boolean updateNotification(long rowId, String title, String description, String
            notificationDateTime) {
        ContentValues args = new ContentValues();
        args.put(KEY_TITLE, title);
        args.put(KEY_DESC, description);
        args.put(KEY_DATE_TIME, notificationDateTime);
        return
                mDb.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
    }

    private static class DatabaseHelper extends SQLiteOpenHelper {
        DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(DATABASE_CREATE);
            Log.i(LOGTAG, "Table created");
        }
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion,
                int newVersion) {
            db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE);
            onCreate(db);
        }
    }
}

4. LogCat Error log

06-10 11:53:24.056: E/AndroidRuntime(8663): FATAL EXCEPTION: main
06-10 11:53:24.056: E/AndroidRuntime(8663): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.tbk.mpeg/com.tbk.mpeg.Notifications}: java.lang.NullPointerException
06-10 11:53:24.056: E/AndroidRuntime(8663):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2114)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2139)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at android.app.ActivityThread.access$700(ActivityThread.java:143)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1241)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at android.os.Handler.dispatchMessage(Handler.java:99)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at android.os.Looper.loop(Looper.java:137)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at android.app.ActivityThread.main(ActivityThread.java:4963)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at java.lang.reflect.Method.invokeNative(Native Method)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at java.lang.reflect.Method.invoke(Method.java:511)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1038)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:805)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at dalvik.system.NativeStart.main(Native Method)
06-10 11:53:24.056: E/AndroidRuntime(8663): Caused by: java.lang.NullPointerException
06-10 11:53:24.056: E/AndroidRuntime(8663):     at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:224)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:164)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at com.tbk.mpeg.database.NotificationsDbAdapter.open(NotificationsDbAdapter.java:40)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at com.tbk.mpeg.Notifications.onCreate(Notifications.java:32)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at android.app.Activity.performCreate(Activity.java:5184)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1094)
06-10 11:53:24.056: E/AndroidRuntime(8663):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2078)
06-10 11:53:24.056: E/AndroidRuntime(8663):     ... 11 more
Dut A.
  • 1,029
  • 11
  • 22
  • The problem is in `NotificationsDbAdapter`, which you have not shown. – CL. Jun 10 '14 at 09:42
  • @CL, NotificationsDbAdapter is shown here. It is the second last in the list of files here. – Dut A. Jun 10 '14 at 09:45
  • No, it's ´NotificationEditor`. – CL. Jun 10 '14 at 10:15
  • I have numbered the code. Please check number 3. – Dut A. Jun 10 '14 at 10:26
  • It's not the adapter. To fix the problem, start with the class that `extends SQLiteOpenHelper` and where you instantiate that class, passing a `null` value to the `super()` constructor as `Context`. – laalto Jun 10 '14 at 10:31
  • 2. and 3. are the same code. – CL. Jun 10 '14 at 10:44
  • @CL, code rectified. Please check code 3 now. – Dut A. Jun 10 '14 at 11:21
  • @laalto, I removed the null initialization from the top part of the code and did it (the initialization) later in the onCreate method (mDbHelper = new NotificationsDbAdapter();) in code sample 2. – Dut A. Jun 10 '14 at 11:31

2 Answers2

0

The Context you passed to SQLiteOpenHelper super constructor was null. This explains the stacktrace.

The Context is null because mCtx is not initialized. It would be initialized in this method you never call:

public void notificationsDbAdapter(Context ctx) {
    this.mCtx = ctx;
}

Change it to a constructor:

public NotificationsDbAdapter(Context ctx) {
    this.mCtx = ctx;
}

And update the references to pass in a Context, e.g.:

new NotificationsDbAdapter(this)
laalto
  • 150,114
  • 66
  • 286
  • 303
  • Even after removing that initialization, the code still doesn't work. – Dut A. Jun 10 '14 at 09:53
  • @Dadani You are not supposed to *remove* anything, you are supposed to *add* a valid `Context` reference. – CL. Jun 10 '14 at 10:45
  • @laalto, I made changes that you suggested and partially my problem is likely to be solved. I can now navigate to Notifications activity but from there to NotificationsEditor, I get an error and the app crashes again. Is this line of code wrong (**NotificationEditor.java**, onCreate method). LogCat tells me it has a problem. 'mRowId = savedInstanceState != null ? savedInstanceState.getLong(NotificationsDbAdapter.KEY_ROWID) : null;' – Dut A. Jun 10 '14 at 14:25
0

You are calling the wrong constructor of the NotificationsDbAdapter class:

    mDbHelper = new NotificationsDbAdapter();

You must use the constructor with the Context parameter; otherwise, the context object is null, and you get the NullPointerException.

The parameter-less constructor is created automatically by the Java compiler because the base class (Object) has one. To prevent it from being used accidentally, create your own, but make it private:

private NotificationsDbAdapter() {
    // never called
}
CL.
  • 173,858
  • 17
  • 217
  • 259