51

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

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

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

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

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

4

3 回答 3

61

注意:这篇文章已经更新为包含JobSchedulerAndroid Lollipop 版本的 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和一个可取消的pendingIntent。让工作每天alarmManager重复(第三个参数),但不精确,因此 CPU 确实在间隔后大约唤醒但不完全(它让操作系统选择最佳时间,从而减少电池消耗)。第一次启动警报(以及服务)将是您选择的时间。recurringAlarmupdateTime

  • 最后但并非最不重要的一点:这是消除重复警报的方法

    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);or jobScheduler.cancelAll();

于 2012-01-03T14:30:29.760 回答
5

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

于 2012-01-03T14:05:34.217 回答
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。

于 2018-08-14T13:07:58.877 回答