Mantener un servicio funcionando incluso cuando el teléfono está dormido?


Tengo un Servicio en mi aplicación que está diseñado para ejecutarse cada 10 minutos. Básicamente revisa nuestros servidores para ver si todo está funcionando correctamente y notifica al usuario de cualquier problema. Creé esta aplicación para uso interno en nuestra empresa.

Mi compañero de trabajo usó la aplicación durante el fin de semana largo y notó que no se realizaron comprobaciones cuando el dispositivo se durmió. Tenía la impresión de que el Servicio debía seguir funcionando en segundo plano. hasta que llame explícitamente stopService() en mi código.

Así que, en última instancia, mi objetivo es tener el servicio en ejecución hasta que el usuario pulsa el botón de apagado en la aplicación o mata el proceso.

He oído hablar de algo llamado WakeLock que está destinado a evitar que la pantalla se apague, que no es lo que quiero. Luego escuché de otra cosa llamadaWakeLock parcial , que mantiene la CPU funcionando incluso cuando el dispositivo está dormido. Esto último suena más cercano a lo que necesito.

¿Cómo puedo adquirir este WakeLock y cuando debo liberarlo y hay otras formas de evitar esto?

Author: Octavian Damiean, 2012-01-03

3 answers

Nota: Este post ha sido actualizado para incluir la API JobScheduler de la versión Android Lollipop. La siguiente sigue siendo una forma viable, pero puede considerarse obsoleta si estás apuntando a Android Lollipop y más allá. Ver la segunda mitad para la alternativa JobScheduler.

Una forma de hacer tareas recurrentes es esta:

  • Crear una clase AlarmReceiver

    public class AlarmReceiver extends BroadcastReceiver 
    {
        @Override
        public void onReceive(Context context, Intent intent) 
        {
            Intent myService = new Intent(context, YourService.class);
            context.startService(myService);
        }
    }
    

    Siendo YourService tu servicio; -)

Si necesita un wake lock para su tarea, es aconsejable ampliar desde WakefulBroadcastReceiver. ¡No olvides añadir el permiso WAKE_LOCK en tu Manifiesto en este caso!

  • Crear una Intent Pendiente

Para iniciar su sondeo recurrente, ejecute este código en su actividad:

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

Este código establece un alarm y un pendingIntent cancelable. El alarmManager consigue que el trabajo repita el recurringAlarm todos los días (tercer argumento), pero es inexacto por lo que la CPU se despierta aproximadamente después del intervalo, pero no exactamente (Permite al sistema operativo elegir el momento óptimo, lo que reduce el consumo de batería). La primera vez que se inicie la alarma (y por lo tanto el servicio) será el momento que elija ser updateTime.

  • Por último, pero no menos importante: aquí está cómo matar la alarma recurrente

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

Este código crea una copia de su (probablemente) alarma existente y le dice a alarmManager que cancele todas las alarmas de ese tipo.

  • por supuesto, también hay algo que hacer en el Manifest:

Incluya estas dos líneas

  < receiver android:name=".AlarmReceiver"></receiver>
  < service android:name=".YourService"></service>

Dentro de la etiqueta < application>. Sin él, el sistema no acepta el inicio de alarma recurrente de un servicio.


Comenzando con el lanzamiento de Android Lollipop, hay una nueva forma de resolver esta tarea con elegancia. Esto también hace que sea más fácil realizar una acción solo si se cumplen ciertos criterios, como el estado de la red.

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

También puede proporcionar una fecha límite con setOverrideDeadline(maxExecutionDelayMillis).

Para deshacerse de tal tarea, simplemente llame jobScheduler.cancel(mJobId); o jobScheduler.cancelAll();.

 59
Author: stefan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-02 11:07:57

Yo habría recomendado, si la construcción de esta aplicación desde el principio para utilizar un componente del lado del servidor (sí, también necesitaría supervisión!) y enviar notificaciones push, el sondeo nunca es una solución confiable.

 5
Author: Lee Hambley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-03 14:05:34

Desde la documentación de Android en modo descanso sucede lo siguiente: ( https://developer.android.com/training/monitoring-device-state/doze-standby):

The system ignores wake locks.

The system does not allow JobScheduler to run.

Android ignora AlarmManager también a menos que estén en setAndAllowWhileIdle() or setExactAndAllowWhileIdle().

Network access is suspended.

Así que la única manera es usar FCM en alta prioridad o AlarmManager con setAndAllowWhileIdle() o setExactAndAllowWhileIdle().

 2
Author: Saba Jafarzadeh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-08-14 13:07:58