web-dev-qa-db-fra.com

Garder un service en marche même lorsque le téléphone est en veille?

J'ai un service dans mon application qui est conçu pour fonctionner toutes les 10 minutes. Il vérifie essentiellement sur nos serveurs pour voir si tout fonctionne correctement et informe l'utilisateur de tout problème. J'ai créé cette application pour un usage interne dans notre entreprise.

Mon collègue a utilisé l'application pendant le long week-end et a remarqué qu'aucune vérification n'a été effectuée lorsque l'appareil s'est endormi. J'avais l'impression que le service était censé continuer à fonctionner en arrière-plan jusqu'à ce que j'appelle explicitement stopService() dans mon code.

En fin de compte, mon objectif est de faire fonctionner le service jusqu'à ce que l'utilisateur appuie sur le bouton d'arrêt de l'application ou tue le processus.

J'ai entendu parler de quelque chose appelé WakeLock qui est censé empêcher l'écran de s'éteindre, ce qui n'est pas ce que je veux. J'ai alors entendu parler d'une autre chose appelée WakeLock partiel, qui maintient le processeur en marche même lorsque l'appareil est en veille. Ce dernier semble plus proche de ce dont j'ai besoin.

Comment puis-je acquérir ce WakeLock et quand dois-je le libérer et y a-t-il d'autres moyens de contourner cela?

51
PaulG

Remarque: Ce message a été mis à jour pour inclure l'API JobScheduler de la version Android Lollipop. Ce qui suit est toujours un moyen viable, mais peut être considéré comme obsolète si vous ciblez Android Lollipop et au-delà. Voir la deuxième moitié pour l'alternative JobScheduler.

Une façon d'effectuer des tâches récurrentes est la suivante:

  • Créer une classe 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 étant votre service ;-)

Si vous avez besoin d'un verrou de réveil pour votre tâche, il est conseillé d'étendre à partir de WakefulBroadcastReceiver . N'oubliez pas d'ajouter la permission WAKE_LOCK Dans votre manifeste dans ce cas!

  • Créer une intention en attente

Pour démarrer votre interrogation récurrente, exécutez ce code dans votre activité:

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

Ce code configure un alarm et un pendingIntent annulable. Le alarmManager obtient la tâche de répéter le recurringAlarm chaque jour (troisième argument), mais inexact pour que le CPU se réveille approximativement après l'intervalle mais pas exactement (Il permet au système d'exploitation de choisir l'heure optimale, ce qui réduit la décharge de la batterie). La première fois que l'alarme (et donc le service) est démarrée sera l'heure à laquelle vous choisissez d'être updateTime.

  • last but not least: voici comment tuer l'alarme récurrente

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

Ce code crée une copie de votre (probablement) alarme existante et indique au alarmManager d'annuler toutes les alarmes de ce type.

  • bien sûr, il y a aussi quelque chose à faire dans le Manifest:

inclure ces deux lignes

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

à l'intérieur de la balise < application>. Sans cela, le système n'accepte pas le début d'une alarme récurrente d'un service.


À partir de la version d'Android Lollipop , il existe une nouvelle façon de résoudre cette tâche avec élégance. Cela facilite également l'exécution d'une action uniquement si certains critères tels que l'état du réseau sont remplis.

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

Vous pouvez également fournir un délai avec setOverrideDeadline(maxExecutionDelayMillis).

Pour vous débarrasser d'une telle tâche, il suffit d'appeler jobScheduler.cancel(mJobId); ou jobScheduler.cancelAll();.

61
stefan

J'aurais recommandé, si la construction de cette application depuis le début utilise un composant côté serveur (oui, aurait également besoin d'une surveillance!) Et d'envoyer des notifications Push, l'interrogation n'est jamais une solution fiable.

5
Lee Hambley

De Android La documentation en mode somnolence se produit: ( https://developer.Android.com/training/monitoring-device-state/doze-standby ):

The system ignores wake locks.

The system does not allow JobScheduler to run.

Android ignore également AlarmManager à moins qu'ils ne soient dans setAndAllowWhileIdle() or setExactAndAllowWhileIdle().

Network access is suspended.

La seule façon est donc d'utiliser FCM en haute priorité ou AlarmManager avec setAndAllowWhileIdle () ou setExactAndAllowWhileIdle ().

3
Saba