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

Pau*_*ulG 51 service android background-process wakelock

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

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

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

我听说过一些叫做WakeLock屏幕不能关闭的东西,这不是我想要的.然后我听说了另一种称为部分WakeLock的东西,即使设备处于睡眠状态,也可以使CPU保持运行.后者听起来更接近我的需要.

我如何获得此WakeLock以及何时应该发布它并且有其他方法可以解决这个问题吗?

ste*_*fan 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);
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    YourService是您的服务;-)

如果您的任务需要唤醒锁定,建议从中扩展WakefulBroadcastReceiver.WAKE_LOCK在这种情况下,不要忘记在Manifest中添加权限!

  • 创建待定意图

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

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
Run Code Online (Sandbox Code Playgroud)

此代码设置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);
    
    Run Code Online (Sandbox Code Playgroud)

此代码会创建(可能)现有警报的副本,并告知alarmManager取消此类警报.

  • 当然还有一些事情要做Manifest:

包括这两行

  < receiver android:name=".AlarmReceiver"></receiver>
  < service android:name=".YourService"></service>
Run Code Online (Sandbox Code Playgroud)

< application>-tag内.没有它,系统不接受服务的周期性警报的开始.


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);
Run Code Online (Sandbox Code Playgroud)

您也可以提供截止日期setOverrideDeadline(maxExecutionDelayMillis).

要摆脱这样的任务,只需打电话jobScheduler.cancel(mJobId);jobScheduler.cancelAll();.

  • 很好的解释,但是你不是仍然需要一个部分的唤醒锁来防止启动服务"入睡"吗?如果你看看[此实现(https://github.com/commonsguy/cwac-wakeful)你看,它也采用了部分激活锁定加报警的方法. (2认同)

Lee*_*ley 5

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