I'm developing an Android app with a reminder function integrated. The notifications work if the phone stay on, but when I turn it off or reboot it I lose all my alarms. I know that this is and Android feature to improve the phone efficency, but I don't know what to do about this, how can I solve this problem?
Here my files:
AlarmService.java
AlarmReceiver.java
BootAlarmReceiver.java
AndroidManifest.xml
"AlarmService.java" is called by "BootAlarmReceiver.java" when the phone is turned on and it should, but it doesn't, reload all my alarms. "AlarmReceiver.java" is called when an alarm is fired from AlarmManager.
Here the code:
AlarmService.java
public class AlarmService extends IntentService {
public AlarmService() {
super("AlarmService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
Calendar calendar = Calendar.getInstance();
FileInputStream fileInputStream = null;
int requestCode, year, month, day, hour, minute;
String note, with;
try {
fileInputStream = openFileInput("my_alarms.csv");
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String row;
while ((row = bufferedReader.readLine()) != null) {
String[] splittedRow = row.split(";");
requestCode = Integer.valueOf(splittedRow[0]);
year = Integer.valueOf(splittedRow[1]);
month = Integer.valueOf(splittedRow[2]);
day = Integer.valueOf(splittedRow[3]);
hour = Integer.valueOf(splittedRow[4]);
minute = Integer.valueOf(splittedRow[5]);
note = splittedRow[6];
with = splittedRow[7];
calendar.set(Calendar.YEAR, year);
calendar.set(Calendar.MONTH, month);
calendar.set(Calendar.DAY_OF_MONTH, day);
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, 0);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(this, AlarmReceiver.class);
alarmIntent.putExtra("note", note + "\nCon: " + with);
alarmIntent.putExtra("title", "My Memo");
alarmIntent.putExtra("alarm", "memo");
//requestCode must be incremental to create multiple reminders
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, requestCode, alarmIntent, 0);
if (calendar.before(Calendar.getInstance())) {
calendar.add(Calendar.DATE, 1);
}
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getStringExtra("alarm").equals("memo")) {
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@SuppressLint("WrongConstant") NotificationChannel notificationChannel = new NotificationChannel("memo_channel", "My Memo", NotificationManager.IMPORTANCE_MAX);
notificationChannel.setDescription("Memo Notification Channel");
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.BLUE);
notificationChannel.setVibrationPattern(new long[]{0, 1000, 500, 1000});
notificationChannel.enableVibration(true);
notificationManager.createNotificationChannel(notificationChannel);
}
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, "memo_channel");
notificationBuilder.setAutoCancel(true)
.setDefaults(Notification.DEFAULT_ALL)
.setWhen(System.currentTimeMillis())
.setShowWhen(true)
.setTicker("Reminder")
.setContentTitle("Memo")
.setContentText(intent.getStringExtra("note"))
.setContentInfo("Information")
.setSmallIcon(R.drawable.ic_alarm);
notificationManager.notify(1, notificationBuilder.build());
}
}
}
BootAlarmReceiver.java
public class BootAlarmReceiver extends BroadcastReceiver {
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onReceive(Context context, Intent intent) {
Intent alarmServiceIntent = new Intent(context, AlarmService.class);
ComponentName service = context.startService(alarmServiceIntent);
if (service == null) {
Log.e("ALARM", "Could not start service");
} else {
Log.e("ALARM", "Could start service");
}
}
}
}
AndroidManifest.xml
<manifest>
<application>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />
//Other code here
<receiver
android:name=".BootAlarmReceiver"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".AlarmReceiver" />
<service android:name=".AlarmService" />
</application>
</manifest>
Please help me, thank you for your time.
EDIT
I found this error when device turn on:
java.lang.RuntimeException: Unable to start receiver com.package.appname.BootAlarmReceiver: java.lang.IllegalStateException: Not allowed to start service Intent { act=REBOOT cmp=com.package.appname/.AlarmService }: app is in background uid UidRecord{a5a4cb2 u0a341 RCVR idle change:uncached procs:1 seq(0,0,0)}
What should I do?
SOLUTION:
Hi guys, I've found the issue I was having, my code is right and it works fine, the problem was in the OS of my device, from Android OS Oreo the command to start a service is changed and is needed a new command syntax:
The changement is in "BootAlarmReceiver.java"
PREVIOUS CODE:
public class BootAlarmReceiver extends BroadcastReceiver {
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onReceive(Context context, Intent intent) {
Intent alarmServiceIntent = new Intent(context, AlarmService.class);
ComponentName service = context.startService(alarmServiceIntent);
if (service == null) {
Log.e("ALARM", "Could not start service");
} else {
Log.e("ALARM", "Could start service");
}
}
}
}
NEW CODE:
public class BootAlarmReceiver extends BroadcastReceiver {
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
Intent alarmServiceIntent = new Intent(context, AlarmService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(alarmServiceIntent);
} else {
context.startService(alarmServiceIntent);
}
}
}
}
So if you are running on Oreo or newer you should use .startForegroundService(yourIntent)
, otherwise you should use .startService(yourIntent)
.
This solution should work also for you.