即使手机处于睡眠状态,也要保持服务运行?

Posted

技术标签:

【中文标题】即使手机处于睡眠状态,也要保持服务运行?【英文标题】:Keep a Service running even when phone is asleep? 【发布时间】:2012-02-01 13:22:29 【问题描述】:

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

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

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

我听说了一个叫做WakeLock 的东西,它是为了防止屏幕关闭,这不是我想要的。然后我听说了另一种叫做 partial WakeLock 的东西,即使设备处于睡眠状态,它也能让 CPU 保持运行。后者听起来更接近我的需要。

我如何获得这个 WakeLock,我应该什么时候释放它,还有其他方法可以解决这个问题吗?

【问题讨论】:

确实,您需要一个部分唤醒锁。查看android PowerManager API 以获取如何获取它的示例。当用户点击你的“关闭”按钮时释放它。 是这样想的,但是关于 WakeLock 的最大障碍似乎是电池消耗。我猜部分 WakeLock 在这方面不会像“完整” WakeLock 那样糟糕。但我可能是错的。 如果有人感兴趣,请在以下链接中找到有关“睡眠”过程的更多信息:***.com/q/5120185/1030185 Run a service in the background forever..? Android 的可能副本 【参考方案1】:

注意:这篇文章已更新为包含 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

要开始您的循环轮询,请在您的活动中执行以下代码:

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 确实在间隔后大约唤醒但不完全(它让操作系统选择最佳时间,从而减少电池消耗)。第一次启动警报(以及服务)将是您选择的时间为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>

&lt; application&gt;-标签内。没有它,系统不接受服务的周期性报警的启动。


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

【讨论】:

如果您要添加一些解释最后两个代码块的作用,我会赞成这个答案。 补充了一点。现在有用吗? 我正在阅读这篇文章,并会试一试。我特别喜欢官方 Android 文档中的以下行:“只要警报接收器的 onReceive() 方法正在执行,警报管理器就会持有 CPU 唤醒锁”。看来这正是我所需要的。 很好的解释,但是你不是还需要一个部分唤醒锁来防止启动的服务“入睡”吗?如果您查看this implementation,您会发现它还使用了部分唤醒锁加警报方法。 我认为在 this 的情况下不需要额外的唤醒锁。这只是这里发生的一些数据拉取,因此该服务的寿命很短,应该可以很快完成它的工作。【参考方案2】:

如果从一开始就构建此应用程序以使用服务器端组件(是的,还需要监控!)并发送推送通知,我会建议,轮询绝不是可靠的解决方案。

【讨论】:

好主意,但有些人没有这个选项。就我而言,它可以工作,因为服务器在我们的办公室并且我可以访问它们,如果您需要每 10 分钟更新一次来自外部来源的数据怎么办?我仍然想知道如何保持服务运行。感谢您的回复。 当然,只是我之前曾尝试过把水推上山,最后决定停止针对操作系统(诚然是 ios)并“正确”实施它(尽管它确实要求更多的基础设施开销) 我在这里发布了一个非常干净漂亮的解决方案。希望能帮助到你。 ***.com/questions/9029040/…【参考方案3】:

在打盹模式下的 Android 文档中会发生以下情况:(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。

【讨论】:

Saba,你知道android服务在打盹模式下是否也会暂停吗?我假设是这样。所有线程都应该暂停,但我想听听你的意见。假设我们有一个 android 服务正在做繁重的工作,但与网络无关。在打盹模式下,服务会暂停吗? 是的,他们将被暂停。 似乎前台服务在打盹模式下保持活动状态。网络操作停止,但如果声明为前台,它可以继续工作。这个例外很有趣。 ***.com/questions/56866806/… @j2emanue 请注意,这也取决于 Android 版本。

以上是关于即使手机处于睡眠状态,也要保持服务运行?的主要内容,如果未能解决你的问题,请参考以下文章

即使手机被锁定/睡眠也能保持应用程序工作 - Ionic4

哪些事件可以唤醒处于睡眠状态的 Android 设备?

当手机进入睡眠状态时,颤振热重载停止工作

Android 在设备处于睡眠状态时获取 GPS 坐标更新(屏幕已关闭)

安卓 C2DM 睡眠手机

设备处于睡眠状态时调用 didReceiveApplicationContext?