E. A. Bagby
E. A. Bagby

Reputation: 930

Context Error when making call from Broadcast Receiver

My app checks the date in onResume to see if a method named changeReminder, in the MainActivity, needs to be called. That is, if the date is different from the stored date, changeReminder is called (changeReminder is called from other methods also, such as a refresh button). changeReminder in turn calls the Reminder class which will create a new reminder when the instance is created.

Now, I'm adding an alarm, broadcast receiver, and a notification to the app. Because the change reminder is called from the MainActivity, I created an instance of the activity in my BroadcastReceiver class, but because the reminder class requires the context (which has always been from the MainActivity) it is throwing an error. Is there a way to successfully pass the context from the Broadcast Receiver in this case?? I think I included the relevant code below, but I can add more if necessary...

Log from once the alarm is triggered:

05-02 13:23:00.712  15511-15511/com.mycompany.dudesmyreminders I/System.out﹕ onReceive
05-02 13:23:00.722  15511-15511/com.mycompany.dudesmyreminders I/System.out﹕ start changeReminder
05-02 13:23:00.722  15511-15511/com.mycompany.dudesmyreminders I/System.out﹕ start Reminder
05-02 13:23:00.752  15511-15511/com.mycompany.dudesmyreminders E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.mycompany.dudesmyreminders, PID: 15511
    java.lang.RuntimeException: Unable to start receiver com.mycompany.dudesmyreminders.ReminderReceiver: java.lang.NullPointerException: Attempt to invoke virtual method 'android.database.sqlite.SQLiteDatabase android.content.Context.openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase$CursorFactory, android.database.DatabaseErrorHandler)' on a null object reference
            at android.app.ActivityThread.handleReceiver(ActivityThread.java:2726)
            at android.app.ActivityThread.access$1700(ActivityThread.java:144)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1449)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:155)
            at android.app.ActivityThread.main(ActivityThread.java:5696)
            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:1028)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)
     Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.database.sqlite.SQLiteDatabase android.content.Context.openOrCreateDatabase(java.lang.String, int, android.database.sqlite.SQLiteDatabase$CursorFactory, android.database.DatabaseErrorHandler)' on a null object reference
            at android.content.ContextWrapper.openOrCreateDatabase(ContextWrapper.java:267)
            at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:223)
            at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase(SQLiteOpenHelper.java:163)
            at com.mycompany.dudesmyreminders.dbDataSource.open(dbDataSource.java:39)
            at com.mycompany.dudesmyreminders.Reminder.<init>(Reminder.java:37)
            at com.mycompany.dudesmyreminders.MainActivity.changeReminder(MainActivity.java:283)
            at com.mycompany.dudesmyreminders.ReminderReceiver.onReceive(ReminderReceiver.java:23)
            at android.app.ActivityThread.handleReceiver(ActivityThread.java:2712)
            at android.app.ActivityThread.access$1700(ActivityThread.java:144)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1449)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:155)
            at android.app.ActivityThread.main(ActivityThread.java:5696)
            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:1028)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:823)

Alarm manager in the MainActivity onCreate:

//Alarm manager for notifications....
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, Calendar.getInstance().get(Calendar.HOUR_OF_DAY));
System.out.println("Hour = " + Calendar.getInstance().get(Calendar.HOUR_OF_DAY));
calendar.set(Calendar.MINUTE, Calendar.getInstance().get(Calendar.MINUTE) + 2);
System.out.println("Minute = " + Calendar.getInstance().get(Calendar.MINUTE));
calendar.set(Calendar.SECOND, 0);
Intent intent1 = new Intent(MainActivity.this, ReminderReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(MainActivity.this, 0,intent1, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) MainActivity.this.getSystemService(MainActivity.ALARM_SERVICE);
am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);

Broadcast Receiver:

public class ReminderReceiver extends BroadcastReceiver {


    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        //throw new UnsupportedOperationException("Not yet implemented");
        System.out.println("onReceive");

        MainActivity ma = new MainActivity();
        ma.changeReminder(context);
        String message = ma.getStoredPreference("CurrentMessage");

        NewReminderNotification.notify(context,message,1);


    }
}

changeReminder in the Main Activity:

public void changeReminder(Context context) {
    System.out.println("start changeReminder");

    Reminder reminder = new Reminder(this);

    // Save the message to the Shared Preferences file
    int altitude = getStoredPreferenceInt(SEEKBAR_CURRENT_INT);
    savePreference("CurrentMessage", reminder.determineReminder(altitude));
    savePreferenceInt(DBDATA_ID, (int) reminder.dbData.getId());
    //populates the reminder dbData for current session
    //DBDATA_ID is only changed in the line above
    dbDataCurrentReminder = datasource.getDB_Row_FromID(dbRTable, (long) getStoredPreferenceInt(DBDATA_ID));

    //display the message just saved
    DisplaySavedMessage();

    // save the current date to Preferences
    saveDate();
    setToggles(0);

}

Reminder Constructor:

public Reminder(Context context) {
    System.out.println("start Reminder");
    ds = new dbDataSource(context);
    ds.open();
    DatabaseVersion = ds.DatabaseVersion;

}

Upvotes: 1

Views: 2932

Answers (2)

CommonsWare
CommonsWare

Reputation: 1006664

There are a few issues with your code.

MainActivity ma = new MainActivity();

First, NEVER create an instance of an Activity, Service, or ContentProvider yourself. Those are only to be instantiated by the framework, not you. This is why your instance does not work.

Second, do not do disk I/O or network I/O on the main application thread, as your UI will be frozen while that I/O is going on. onReceive() of a BroadcastReceiver is called on the main application thread, and you are attempting (in vain) to do database I/O on that thread.

Is there a way to successfully pass the context from the Broadcast Receiver in this case?

You are welcome to make the method be a static method.

However, a better solution would be for you to move this code into an IntentService. First, it gives you a safe, managed background thread on which to do this work. Second, it works in conjunction with the WakefulBroadcastReceiver that you should be using, to ensure that the device can remain awake long enough for you to do this database I/O. Always use WakefulBroadcastReceiver, my WakefulIntentService, or your own WakeLock with RTC_WAKEUP or ELAPSED_REALTIME_WAKEUP alarms.

Upvotes: 1

Elltz
Elltz

Reputation: 10859

try this put changeReminder(Context context) method block in your broadcast receiver instead of Activity and get rid of this line

MainActivity ma = new MainActivity();
ma.changeReminder(ma.getApplicationContext());

and make it

changeReminder(context); //you already have a context

Also idk if you know but anywhere you call this you can call your Context unless it takes an Activity class instance .. this is refering to the this you called in changeReminder also put this method getStoredPreference("CurrentMessage"); block in your Receiver class or a singleton class

Upvotes: 1

Related Questions