Android Alarm Manager повторяется в определенное время
У меня возникли проблемы с диспетчером аварий в Android. Так что я пытаюсь сделать так, чтобы будильник повторялся для запуска вставки БД каждый день около 12.01.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 0 );
calendar.set(Calendar.MINUTE, 1);
notificationCount = notificationCount + 1;
AlarmManager mgr = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent notificationIntent = new Intent(context,
ReminderAlarm.class);
notificationIntent.putExtra("NotifyCount", notificationCount);
PendingIntent pi = PendingIntent.getBroadcast(context,
notificationCount, notificationIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pi);
В общем, я придумал этот код. Однако диспетчер аварийных сигналов снова запускается через минуту, когда я его установил.
Допустим, я запускаю приложения 01/10/2014 5.48PM. Я хотел, чтобы это запускало вставку БД при onReceive каждый день после того, как я установил его только в 12.01. Но каким-то образом диспетчер аварийных сигналов запускается в 10.10.2014 в 5.49 вечера, то есть через одну минуту после того, как я его установил, и он перестал работать.
Интересно, какую часть я сделал неправильно.
Заранее спасибо.
РЕДАКТИРОВАТЬ
Повторяющийся класс Для этого класса он будет запускать диспетчер аварий каждый день и передавать переменные в класс сигналов напоминаний для вставки БД.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recurring);
context = this;
buildListView();
if(!alarmInitialized(this)) {
scheduleAlarms(this);
}
}
// And the few methods you suggested to schedule the alarm
public static void scheduleAlarms(Context context) {
Calendar calendar = Calendar.getInstance();
if (hasRunnedToday(context)) { // if the alarm has run this day
calendar.add(Calendar.DATE, 1); // schedule it to run again starting
// tomorrow
}
long firstRunTime = calendar.getTimeInMillis();
AlarmManager mgr = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent notificationIntent = new Intent(context, ReminderAlarm.class);
PendingIntent pi = PendingIntent.getActivity(context, 0,
notificationIntent, 0);
mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP, firstRunTime,
AlarmManager.INTERVAL_DAY, pi);
ComponentName receiver = new ComponentName(context, BootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
Класс BootReceiver
public void onReceive(Context context, Intent i) {
if (i.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
Recurring.scheduleAlarms(context);
}
}
Класс ReminderAlarm В основном для этого класса он просто захватывает переменную, переданную из класса Recurring, и выполняет вставку БД. Я вставил несколько Toast.makeText, чтобы проверить, извлекает ли он данные, но не повезло, протестировав их.
public class ReminderAlarm extends BroadcastReceiver {
private NotificationManager mNotificationManager;
private Notification notification;
@Override
public void onReceive(Context context, Intent intent) {
String recurID = null;
String recurStartDate = null;
String currentDate = null;
String description = null;
String type = null;
String amount = null;
String categoryName = null;
String frequencyStr = null;
String nextPaymentDate = null;
SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
DatabaseAdapter mDbHelper = new DatabaseAdapter(context);
mDbHelper.createDatabase();
mDbHelper.open();
RecurringController rc = new RecurringController(mDbHelper.open());
ArrayList<RecurringModel> recur_list = rc.getAllRecurring();
// THIS PART TO GET DATA FROM DATABASE
for (int i = 0; i < recur_list.size(); i++) {
recurID = recur_list.get(i).getRecurringID();
recurStartDate = recur_list.get(i).getRecurringStartDate();
currentDate = dateFormat.format(new Date());
description = recur_list.get(i).getRecurringDesc();
type = recur_list.get(i).getRecurringType();
amount = Float.toString(recur_list.get(i).getRecurringAmount());
categoryName = recur_list.get(i).getCategoryID();
frequencyStr = recur_list.get(i).getFrequency();
Toast.makeText(context,
description, Toast.LENGTH_LONG)
.show();
Toast.makeText(context,
recurStartDate Toast.LENGTH_LONG)
.show();
Calendar cal = Calendar.getInstance();
try {
cal.setTime(dateFormat.parse(recurStartDate));
if (frequencyStr.equals("Daily")) {
cal.add(Calendar.DAY_OF_MONTH, 1);
nextPaymentDate = dateFormat.format(cal.getTimeInMillis());
cal.add(Calendar.DAY_OF_MONTH, -1);
} else if (frequencyStr.equals("Weekly")) {
cal.add(Calendar.WEEK_OF_YEAR, 1);
nextPaymentDate = dateFormat.format(cal.getTimeInMillis());
cal.add(Calendar.WEEK_OF_YEAR, -1);
} else if (frequencyStr.equals("Monthly")) {
cal.add(Calendar.MONTH, 1);
nextPaymentDate = dateFormat.format(cal.getTimeInMillis());
cal.add(Calendar.MONTH, -1);
} else if (frequencyStr.equals("Yearly")) {
cal.add(Calendar.YEAR, 1);
nextPaymentDate = dateFormat.format(cal.getTimeInMillis());
cal.add(Calendar.YEAR, -1);
}
} catch (ParseException e) {
e.printStackTrace();
}
// If dates match then execute the SQL statements
if (currentDate.equals(nextPaymentDate)) {
// mDbHelper.createDatabase();
// mDbHelper.open();
TransactionRecModel trm = new TransactionRecModel();
CategoryController cc = new CategoryController(mDbHelper.open());
trm.setDate(currentDate);
trm.setTransDescription(description);
trm.setType(type);
trm.setAmount(Float.parseFloat(amount));
// Get the categoryID based on categoryName
String catID = cc.getCatIDByName(categoryName);
trm.setCategory(catID);
// Check if the recurring record exists before insert new
// transaction record
boolean recurExist = rc.checkRecurExist(recurStartDate,
description, catID);
if (recurExist == true) {
TransactionRecController trc = new TransactionRecController(
mDbHelper.open());
// Check if the transaction record exists to prevent
// duplication
boolean moveNext = trc.checkTransExist(trm);
if (moveNext == false) {
if (trc.addTransactionRec(trm)) {
// Update recurring start date after insertion of
// transaction
RecurringModel rm = new RecurringModel();
rm.setRecurringID(recurID);
rm.setRecurringStartDate(currentDate);
if (rc.updateRecurringDate(rm)) {
mNotificationManager = (NotificationManager) context
.getSystemService(Context.NOTIFICATION_SERVICE);
PendingIntent contentIntent = PendingIntent
.getActivity(
context,
Integer.parseInt(intent
.getExtras()
.get("NotifyCount")
.toString()),
new Intent(), 0);
notification = new Notification(
R.drawable.ic_launcher, "Notification",
System.currentTimeMillis());
notification.setLatestEventInfo(context,
description, nextPaymentDate,
contentIntent);
mNotificationManager
.notify(Integer.parseInt(intent
.getExtras().get("NotifyCount")
.toString()), notification);
mDbHelper.close();
}
}
}
}
mDbHelper.close();
}
}
mDbHelper.close();
Recurring.updateAlarmLastRun(context);
}
}
Я добавил эту часть кодов в ту часть, которую вы предложили запланировать для вызова будильника в классе BootReceiver. Затем из класса BootReceiver я перезвоню в класс Recurring и класс Alarm Reminder:
ComponentName receiver = new ComponentName(context, BootReceiver.class);
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(receiver,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
1 ответ
Проблема в calendar.getTimeInMillis()
в
mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pi);
Второй аргумент setInexactRepeating
цитируя документ
triggerAtMillis время в миллисекундах, в течение которого будильник должен сначала сработать, используя соответствующие часы (в зависимости от типа будильника). Это неточно: сигнал тревоги не сработает до этого времени, но может быть задержка почти на весь интервал тревоги до первого вызова тревоги.
Это означает, что он будет запущен в первый раз приблизительно через одну минуту после того, как вы
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 0 );
calendar.set(Calendar.MINUTE, 1);
Если вы не хотите, чтобы первый запуск будильника был на следующий день, сделайте calendar.add(Calendar. DATE, 1);`
Что касается того, чтобы он перестал работать, вы перезагрузили устройство?
AlarmCalendar не сохраняется до перезагрузки устройства, вы можете зарегистрировать BroadcastReceiver
получить BOOT_COMPLETED
событие и зарегистрируйте тревогу снова. Проверьте , сохраняется ли Alarm Manager даже после перезагрузки?
Обновление: как вы и просили, вам нужна помощь после просмотра кода.
В вашем BOOT_COMPLETED
Классприемника:
public void onReceive(Context context, Intent i) {
if (i.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
ReminderAlarm.scheduleAlarms(this);
}
}
В вашем классе ReminderAlarm
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.recurring);
if(!alarmInitialized(this) {
scheduleAlarms(this);
}
}
public static void scheduleAlarms(Context context) {
Calendar calendar = Calendar.getInstance();
if(hasRunnedToday(context)) { //if the alarm has run this day
calendar.add(Calendar.DATE, 1); //schedule it to run again starting tomorrow
}
long firstRunTime = calendar.getTimeInMillis();
AlarmManager mgr = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent notificationIntent = new Intent(context, ReminderAlarm.class);
PendingIntent pi = PendingIntent.getActivity(context, 0,
notificationIntent, 0);
mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP,
firstRunTime, AlarmManager.INTERVAL_DAY, pi);
}
public static boolean alarmInitialized(Context context) {
SharedPreferences preferences = context.getSharedPreferences("alarm_prefs", MODE_PRIVATE);
long alarmLastRun = preferences.getLong("AlarmLastRun", -1);
return alarmLastRun != -1;
}
public static void updateAlarmLastRun(Context context) {
SharedPreferences preferences = context.getSharedPreferences("alarm_prefs", MODE_PRIVATE);
preferences.edit()
.putLong("AlarmLastRun", new Date().getTime())
.apply();
}
public static boolean hasRunnedToday(Context context) {
SharedPreferences preferences = context.getSharedPreferences("alarm_prefs", MODE_PRIVATE);
long alarmLastRun = preferences.getLong("AlarmLastRun", -1);
if(alarmLastRun == -1) {
return false;
}
//check by comparing day, month and year
Date now = new Date();
Date lastRun = new Date(alarmLastRun);
return now.getTime() - lastRun.getTime() < TimeUnit.DAYS.toMillis(1);
}
Каждый раз, когда срабатывает сигнализация класса Reminder, вы должны позвонить updateAlarmLastRun
чтобы обновить последний раз, когда сработал будильник, это необходимо, потому что будильник может быть запланирован на день, и пользователь перезагружает устройство до срабатывания будильника, в этом случае мы не хотим использовать calendar.add(Calendar.DATE, 1);
так как это пропустит день.
На ваше Manifest.xml
<receiver android:name=".BootReceiver" android:enabled="true" android:exported="false" android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Заметки:
- Ты не должен делать
context = this
если context является полем класса, так как объект содержит ссылку на свое полеcontext
а такжеcontext
поле содержит ссылку на объект, который будет протекать - Ваш
Receiver
"onReceive" не имеет дополнительных функций, которые, как вы предполагали, имеют, например, "messagesCount" при получении системой после завершения загрузки вашего устройства. - Как только ваш будильник запускает звонок
updateAlarmLastRun
Надеюсь, что это поможет