AlarmManager.setRepeating不準確的問題,被setWindow()或者setExact()替代

背景:

當我們想讓Android應用程序定時爲做一件工作時,我們往往會在一個BroadcastReceiver中使用AlarmManager.setRepeating()方法來實現。在API 19(即Kitkat)之後,這一方法將不再準確地保證每次工作都在你設置的時間開始。

 

解釋:

Note: Beginning in API 19, the trigger time passed to this method is treated as inexact: the alarm will not be delivered before this time, but may be deferred and delivered some time later. The OS will use this policy in order to "batch" alarms together across the entire system, minimizing the number of times the device needs to "wake up" and minimizing battery use. In general, alarms scheduled in the near future will not be deferred as long as alarms scheduled far in the future.

With the new batching policy, delivery ordering guarantees are not as strong as they were previously. If the application sets multiple alarms, it is possible that these alarms' actual delivery ordering may not match the order of their requesteddelivery times. If your application has strong ordering requirements there are other APIs that you can use to get the necessary behavior; see setWindow(int, long, long, PendingIntent) and setExact(int, long, PendingIntent).

原來,操作系統爲了節能省電,將會調整alarm喚醒的時間。故通過AlarmManager.setRepeating()方法將不再保證你定義的工作能按時開始。

 

解決辦法:

1. 按照官方網站說的,調用API 19之後出現的setWindow()或者setExact()方法。

2、先設置一次性的鬧鐘,收到鬧鐘廣播的提醒之後,再設置下一次的;(以下是部分關鍵代碼,僅供參考)

  public static void setAlarmTime(Context context, long timeInMillis, Intent intent) {
        AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        PendingIntent sender = PendingIntent.getBroadcast(context, intent.getIntExtra("id", 0),
                intent, PendingIntent.FLAG_UPDATE_CURRENT);
        int interval = (int) intent.getLongExtra("intervalMillis", 0);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            am.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, sender);
        }
    }
public class AlarmReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        String msg = intent.getStringExtra("msg");
        int id = intent.getIntExtra("id", -1);

        long intervalMillis = intent.getLongExtra("intervalMillis", 0);
        if (intervalMillis != 0) {
            AlarmManagerUtil.setAlarmTime(context, System.currentTimeMillis() + intervalMillis,
                    intent);
        }
    }
}
/**
 * @param flag            週期性時間間隔的標誌,flag = 0 表示一次性的鬧鐘, flag = 1 表示每天提醒的鬧鐘(1天的時間間隔),flag = 2
 *                        表示按周每週提醒的鬧鐘(一週的週期性時間間隔)
 * @param hour            時
 * @param minute          分
 * @param id              鬧鐘的id
 * @param week            week=0表示一次性鬧鐘或者按天的週期性鬧鐘,非0 的情況下是幾就代表以周爲週期性的周幾的鬧鐘
 * @param tips            鬧鐘提示信息
 * @param soundOrVibrator 2表示聲音和震動都執行,1表示只有鈴聲提醒,0表示只有震動提醒
 */   
 public static void setAlarm(Context context, int flag, int hour, int minute, int id, int
            week, String tips, int soundOrVibrator) {
        AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Calendar calendar = Calendar.getInstance();
        long intervalMillis = 0;
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), calendar.get
                (Calendar.DAY_OF_MONTH), hour, minute, 3);
        if (flag == 0) {
            intervalMillis = 0;
        } else if (flag == 1) {
            intervalMillis = 24 * 3600 * 1000;
        } else if (flag == 2) {
            intervalMillis = 24 * 3600 * 1000 * 7;
        }
        Intent intent = new Intent(ALARM_ACTION);
        intent.putExtra("intervalMillis", intervalMillis);
        intent.putExtra("msg", tips);
        intent.putExtra("hour", hour);
        intent.putExtra("minute", minute);
        intent.putExtra("id", id);
        intent.putExtra("soundOrVibrator", soundOrVibrator);
        PendingIntent sender = PendingIntent.getBroadcast(context, id, intent, PendingIntent
                .FLAG_IMMUTABLE);

        LogUtils.d("utils", "clock裏的week:" + week);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            am.setExact(AlarmManager.RTC_WAKEUP, calMethod(week, calendar.getTimeInMillis()), sender);
        } else {
            if (flag == 0) {
                am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender);
            } else {
                am.setRepeating(AlarmManager.RTC_WAKEUP, calMethod(week, calendar.getTimeInMillis
                        ()), intervalMillis, sender);
            }
        }
    }
public static void cancelAlarm(Context context, String action, int id) {
    Intent intent = new Intent(action);
    PendingIntent pi = PendingIntent.getBroadcast(context, id, intent, PendingIntent
            .FLAG_UPDATE_CURRENT);
    AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    am.cancel(pi);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章