即使手机处于睡眠状态,也可以保持服务正常运行?

时间:2012-01-03 14:00:24

标签: android service background-process wakelock

我的应用程序中有一个服务,它设计为每10分钟运行一次。它基本上检查我们的服务器,看看是否一切正常运行,并通知用户任何问题。我创建了这个应用程序供我们公司内部使用。

我的同事在漫长的周末使用了该应用程序,并注意到该设备进入睡眠状态时未执行任何检查。我的印象是该服务应该在后台继续运行,直到我在代码中明确地调用stopService()

所以最终,我的目标是让服务运行,直到用户点击应用程序中的关闭按钮或终止进程。

我听说过一种名为WakeLock的东西,意在阻止屏幕关闭,这不是我想要的。然后我听说了另一个叫做部分WakeLock 的东西,它可以让CPU在设备处于睡眠状态时保持运行。后者听起来更接近我的需要。

我如何获得此WakeLock以及何时应该发布它?还有其他解决方法吗?

3 个答案:

答案 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和可取消的pendingIntentalarmManager得到的工作是每天重复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上使用。