我需要每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 毫秒)。
但是当我在小米Redmi Note 3 Pro(5.1.1)上运行app时 - 偏差可以达到 15秒!
例如,我在我的日志文件中看到:第一次运行是在1467119934477(RTC时间),第二次是在1467120541683.差异是 607_206 毫秒,而不是 600_000 ,按计划进行!
我缺少什么?有什么方法可以模拟系统警报的行为(它是可以描述我的大头钉的最接近的用例)?
PS。我对PendingIntent = PendingIntent.getService(context, 0, myIntent, 0);
答案 0 :(得分:30)
操作系统会根据您指定的时间选择警报的工作方式。因此,当手机进入“半睡眠”模式时,无需在您希望的时候使用该资源。基本上,它等待操作系统为其打开的“窗口”,然后只有你想要运行的警报才会运行,这就是你遇到时间差的原因。
这是在Marshmallow操作系统上引入的,并将继续在Nougat操作系统上,作为谷歌尝试改进设备电池的一部分。
这就是事情,你有两个选择:
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);
答案 1 :(得分:6)
可能的解决方法可能是这样的: 您在预期时间前约1分钟安排警报,而不是使用Handler.postDelayed来覆盖剩余时间。
在这里您可以找到这种实现的示例。 该活动只是设置了第一个警报:
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);
}
}
我希望它有所帮助。
答案 2 :(得分:3)
回答关于系统警报的问题......
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 意图则会被忽略。
答案 3 :(得分:2)
从API 19(KITKAT)开始,警报传递不准确:操作系统将移动警报以最小化唤醒和电池使用。有新的API支持需要严格交付保证的应用程序;请参阅setWindow(int,long,long,PendingIntent)和setExact(int,long,PendingIntent)。 targetSdkVersion早于API 19的应用程序将继续查看之前在请求时准确传递所有警报的行为。
同时使用setExact():
警报将尽可能>发送到请求的触发时间。
所以它仍然不能保证setExact将是Exact。
答案 4 :(得分:2)
您可以尝试使用AlarmManager.setAlarmClock
也许它可以帮助您。
您需要检查您正在使用哪种类型的BroadcastReceiver,最好使用WakefulBroadcastReceiver
顺便说一下你需要改变逻辑以使用Alarm Manager来支持Android M,你可以这样:
if(Build.VERSION.SDK_INT < 23){
if(Build.VERSION.SDK_INT >= 19) {
setExact(...);
} else {
set(...);
}
} else {
setExactAndAllowWhileIdle(...);
}
答案 5 :(得分:2)
您可以从support.v4调用该方法:
AlarmManagerCompat.setExact(...);
内部实现包含sdk版本的检查。