Reputation: 930
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
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
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