スケジュールされたタスクを10分ごとに計画する必要があります。
Lollipop以降のバージョンsetRepeating()
は不正確であるため、setExact()
を使用し、(アラームの発動時に)新しい正確なアラームを10分で設定します。
_private void setAlarm(long triggerTime, PendingIntent pendingIntent) {
int ALARM_TYPE = AlarmManager.ELAPSED_REALTIME_WAKEUP;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KitKat) {
alarmManager.setExact(ALARM_TYPE, triggerTime, pendingIntent);
} else {
alarmManager.set(ALARM_TYPE, triggerTime, pendingIntent);
}
}
_
triggerTime
が計算されますSystemClock.elapsedRealtime() + 600_000;
アラームが鳴ったら、まず新しいアラームを計画します。その後、スケジュールされたタスクを実行します。
_setAlarm();
mySheduledTask;
_
マニフェストに_WAKE_LOCK
_権限があります。
これをAndroid 4-でテストすると完璧に動作します(偏差は12-15 ミリ秒かもしれません)。
しかし、Xiaomi Redmi Note 3 Pro(5.1.1)でアプリを実行すると、偏差は15秒!
たとえば、ログファイルに次のように表示されます。最初の実行は1467119934477(RTC time)で、2番目-1467120541683でした。違いは607_206ミリ秒ではなく、 600_0、計画どおり!
私は何が欠けていますか?システムアラームの動作をシミュレートする方法は何ですか(私のタックを説明できる最も近いユースケースです)?
PS。PendingIntent = PendingIntent.getService(context, 0, myIntent, 0);
にIntentServiceを使用します
OSは、指定した時間を考慮して、アラームの動作方法を選択します。そのため、電話が「セミスリープ」モードになると、希望するときにリソースを使用する必要がなくなります。基本的に、OSが開くための「ウィンドウ」を待機し、実行するアラームのみが実行されるため、時間のギャップが発生します。
これはMarshmallow OSで導入されたもので、デバイスのバッテリーを改善しようとしているGoogleの一部として、Nougat OSでも継続されます。
ここに、2つのオプションがあります。
JobScheduler
を使用することをお勧めします。これをお勧めします。バッテリーを節約できます)。setExactAndAllowWhileIdle
を使用すると、バッテリーの問題が発生する可能性があります(これを慎重に使用すると、あまりにも多くのアラームがバッテリーに悪影響を及ぼします)。このメソッドは繰り返されないため、pendingIntentが開くサービスで実行される次のジョブを宣言する必要があります。オプション2を選択した場合、ここから始まります。
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
int ALARM_TYPE = AlarmManager.RTC_WAKEUP;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
am.setExactAndAllowWhileIdle(ALARM_TYPE, calendar.getTimeInMillis(), pendingIntent);
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KitKat)
am.setExact(ALARM_TYPE, calendar.getTimeInMillis(), pendingIntent);
else
am.set(ALARM_TYPE, calendar.getTimeInMillis(), pendingIntent);
おそらく、次のような回避策が考えられます。Handler.postDelayedを使用して残り時間をカバーするよりも、アラームを予定時刻の約1分前にスケジュールします。
ここでは、この種の実装の例を見つけることができます。アクティビティは最初のアラームを設定するだけです:
public class MainActivity extends AppCompatActivity {
private static int WAIT_TIME = 60*1000; //1 minute
public static int DELAY_TIME = 10*60*1000; // delay between iterations: 10min
public static String UPDATE_TIME_KEY = "update_time_key";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setAlarm(this,(new Date().getTime())+DELAY_TIME);
}
public static void setAlarm(Context context, long delay) {
long fireDelay = delay-WAIT_TIME;
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
sharedPreferences.edit().putLong(UPDATE_TIME_KEY,delay).apply();
Intent startIntent = new Intent(context, UpdateReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1, startIntent,PendingIntent.FLAG_UPDATE_CURRENT );
AlarmManager alarmManager = (AlarmManager) context.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
int ALARM_TYPE = AlarmManager.RTC;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KitKat) {
alarmManager.setExact(ALARM_TYPE, fireDelay, pendingIntent);
} else {
alarmManager.set(ALARM_TYPE, fireDelay, pendingIntent);
}
}
}
受信者がループを継続するよりも:
public class UpdateReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, Intent intent) {
Log.e("RECEIVED","RECEIVED");
SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
long fireTime = sharedPreferences.getLong(MainActivity.UPDATE_TIME_KEY, (new Date()).getTime());
long fireDelay =(fireTime-(new Date().getTime())>0)?fireTime-(new Date().getTime()):0;
(new Handler()).postDelayed(new Runnable() {
@Override
public void run() {
Log.e("RECEIVED","PERFORMED");
MainActivity.setAlarm(context,(new Date()).getTime()+MainActivity.DELAY_TIME);
}
},fireDelay);
}
}
役に立てば幸いです。
システムアラームに関する質問に答えるには...
Androidの標準の目覚まし時計/デスクトップ時計アプリは、setAlarmClockとsetExactAndAllowWhileIdleの組み合わせを使用します。
次のコードは、通知を更新するために使用されます。
final PendingIntent operation = PendingIntent.getBroadcast(context, 0,
AlarmStateManager.createIndicatorIntent(context), flags);
final AlarmClockInfo info = new AlarmClockInfo(alarmTime, viewIntent);
alarmManager.setAlarmClock(info, operation);
同時に、次のコードを使用して実際のアラームをスケジュールします。
if (Utils.isMOrLater()) {
// Ensure the alarm fires even if the device is dozing.
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent);
} else {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, timeInMillis, pendingIntent)
}
setExactAndAllowWhileIdleで設定された保留中のインテントはアラームをトリガーしますが、setAlarmClockのインテントは単に無視されます。
Support.v4からメソッドを呼び出すことができます。
AlarmManagerCompat.setExact(...);
内部実装には、SDKバージョンによるチェックが含まれています。
AlarmManager.setAlarmClock
多分あなたを助けることができます。
使用しているBroadcastReceiverのタイプを確認する必要があるもう1つのことは、 WakefulBroadcastReceiver
を使用することをお勧めします。
ところで、サポートのためにアラームマネージャーを使用するためのロジックを変更する必要がありますAndroid M、次のようなことができます:
if(Build.VERSION.SDK_INT < 23){
if(Build.VERSION.SDK_INT >= 19) {
setExact(...);
} else {
set(...);
}
} else {
setExactAndAllowWhileIdle(...);
}
From Android documentation of AlarmManager )
API 19(KitKat)以降、アラーム配信は不正確です。OSは、ウェイクアップとバッテリー使用を最小限に抑えるためにアラームをシフトします。厳密な配信保証が必要なアプリケーションをサポートする新しいAPIがあります。 setWindow(int、long、long、PendingIntent)およびsetExact(int、long、PendingIntent)を参照してください。 targetSdkVersionがAPI 19より前のアプリケーションでは、要求されたときにすべてのアラームが正確に配信される以前の動作が引き続き表示されます。
また、setExact()を使用している場合:
アラームは、要求されたトリガー時間に可能な限り近いで配信されます。
したがって、setExactがExactであることはまだ保証されていません。