我的应用程序中有一个服务,它设计为每10分钟运行一次。它基本上检查我们的服务器,看看是否一切正常运行,并通知用户任何问题。我创建了这个应用程序供我们公司内部使用。
我的同事在漫长的周末使用了该应用程序,并注意到该设备进入睡眠状态时未执行任何检查。我的印象是该服务应该在后台继续运行,直到我在代码中明确地调用stopService()
。
所以最终,我的目标是让服务运行,直到用户点击应用程序中的关闭按钮或终止进程。
我听说过一种名为WakeLock
的东西,意在阻止屏幕关闭,这不是我想要的。然后我听说了另一个叫做部分WakeLock 的东西,它可以让CPU在设备处于睡眠状态时保持运行。后者听起来更接近我的需要。
我如何获得此WakeLock以及何时应该发布它?还有其他解决方法吗?
答案 0 :(得分:61)
注意:此帖子已更新,包含Android Lollipop版本的JobScheduler
API。以下仍然是一种可行的方法,但如果你的目标是Android Lollipop,那么可以考虑弃用。请参阅下半部分JobScheduler
替代方案。
执行周期性任务的一种方法是:
创建课程AlarmReceiver
public class AlarmReceiver extends BroadcastReceiver
{
@Override
public void onReceive(Context context, Intent intent)
{
Intent myService = new Intent(context, YourService.class);
context.startService(myService);
}
}
以YourService
为您的服务; - )
如果您的任务需要唤醒锁定,建议从WakefulBroadcastReceiver
延伸。在这种情况下,请不要忘记在您的清单中添加WAKE_LOCK
权限!
要开始重复轮询,请在您的活动中执行以下代码:
Intent myAlarm = new Intent(getApplicationContext(), AlarmReceiver.class);
//myAlarm.putExtra("project_id", project_id); //Put Extra if needed
PendingIntent recurringAlarm = PendingIntent.getBroadcast(getApplicationContext(), 0, myAlarm, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
Calendar updateTime = Calendar.getInstance();
//updateTime.setWhatever(0); //set time to start first occurence of alarm
alarms.setInexactRepeating(AlarmManager.RTC_WAKEUP, updateTime.getTimeInMillis(), AlarmManager.INTERVAL_DAY, recurringAlarm); //you can modify the interval of course
此代码设置alarm
和可取消的pendingIntent
。 alarmManager
得到的工作是每天重复recurringAlarm
(第三个参数),但是不精确所以CPU确实在间隔后大约醒来但不完全醒来(它让OS选择最佳时间,减少电池消耗)。第一次启动警报(以及服务)将是您选择updateTime
的时间。
最后但并非最不重要:以下是如何杀死定期警报
Intent myAlarm = new Intent(getApplicationContext(), AlarmReceiver.class);
//myAlarm.putExtra("project_id",project_id); //put the SAME extras
PendingIntent recurringAlarm = PendingIntent.getBroadcast(getApplicationContext(), 0, myAlarm, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarms.cancel(recurringAlarm);
此代码会创建(可能)现有警报的副本,并告知alarmManager
取消此类警报。
Manifest
:包括这两行
< receiver android:name=".AlarmReceiver"></receiver>
< service android:name=".YourService"></service>
在< application>
- 标记内。没有它,系统不接受服务的周期性警报的开始。
从 Android Lollipop 版本开始,有一种优雅地解决此任务的新方法。 如果满足某些标准(如网络状态),这也可以更轻松地执行操作。
// wrap your stuff in a componentName
ComponentName mServiceComponent = new ComponentName(context, MyJobService.class);
// set up conditions for the job
JobInfo task = JobInfo.Builder(mJobId, mServiceComponent)
.setPeriodic(mIntervalMillis)
.setRequiresCharging(true) // default is "false"
.setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED) // Parameter may be "ANY", "NONE" (=default) or "UNMETERED"
.build();
// inform the system of the job
JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(task);
您还可以提供setOverrideDeadline(maxExecutionDelayMillis)
的截止日期。
要摆脱这样的任务,只需致电jobScheduler.cancel(mJobId);
或jobScheduler.cancelAll();
。
答案 1 :(得分:5)
我建议,如果从一开始构建此应用程序以使用服务器端组件(是的,还需要监控!)并发送推送通知,则轮询永远不是一个可靠的解决方案。
答案 2 :(得分:3)
从Android文档以打ze模式发生以下情况:(https://developer.android.com/training/monitoring-device-state/doze-standby):
The system ignores wake locks.
The system does not allow JobScheduler to run.
Android也将忽略AlarmManager,除非它们位于setAndAllowWhileIdle() or setExactAndAllowWhileIdle().
Network access is suspended.
因此,唯一的方法是在具有高优先级的FCM上使用,或在带有setAndAllowWhileIdle()或setExactAndAllowWhileIdle()的AlarmManager上使用。