Android Alarm Manager设置在特定时间重复

时间:2014-10-01 09:50:05

标签: java android date alarmmanager recurring

我在Android中遇到警报管理器的问题。所以我要做的就是设置警报重复每天12.01AM左右运行数据库插入。

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);

所以基本上我已经提出了这些代码。但是,警报管理器在我设置它之后再次执行。

我们说我在2014年1月10日运行应用程序5.48PM。我希望这在我每天在12.01AM左右设置后每天onReceive时运行数据库插入。但不知何故,警报管理器在2014年10月1日下午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类和Reminder Alarm类:

ComponentName receiver = new ComponentName(context, BootReceiver.class);
    PackageManager pm = context.getPackageManager();

    pm.setComponentEnabledSetting(receiver,
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            PackageManager.DONT_KILL_APP);

1 个答案:

答案 0 :(得分:4)

问题出在calendar.getTimeInMillis()

mgr.setInexactRepeating(AlarmManager.RTC_WAKEUP,
            calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pi);

setInexactRepeating引用文档

的第二个参数
  

triggerAtMill使用适当的时钟(取决于警报类型),警报应首先关闭的时间(以毫秒为单位)。这是不准确的:在此时间之前警报不会触发,但在第一次调用警报之前可能会有几乎整个警报间隔的延迟。

意味着它会因为

而在设置后一分钟第一次运行
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.set(Calendar.HOUR_OF_DAY, 0 );
calendar.set(Calendar.MINUTE, 1);

如果你想第一次发出警报就要做第二天了 calendar.add(日历。日期,1);`

至于它停止工作,你重启设备吗? AlarmCalendar警报不会持续到设备重启,您可以注册BroadcastReceiver以接收BOOT_COMPLETED事件并再次注册警报 does Alarm Manager persist even after reboot?

更新:根据您的要求,我们在审核您的代码后提供了一些帮助

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);
}

每次提醒类警报运行时,您应该拨打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>

注意:

  1. 如果上下文是一个类字段,则不应该context = this,因为该对象包含对其字段context的引用,而context字段包含对将泄漏的对象的引用< / LI>
  2. 当您的设备完成启动时,您的Receiver'onReceive`没有您认为具有类似“notificationCount”onceceive的额外内容。
  3. 警报响起后,请致电updateAlarmLastRun
  4. 希望这有助于