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