Accès rapide :
Démarrage du JobService au boot du périphérique
Lancement, une unique fois, de notre JobService
Lancement périodique de notre JobService
Solution de contournement pour avoir une période de déclenchement inférieure à 15 minutes
Depuis Android 8.0 Oreo (API 26), démarrer, lors du boot du système, un service tournant dans un thread en arrière plan est devenu impossible. La raison principale étant l'économie de la batterie : effectivement, mal codé, un thread tournant en permanence en arrière plan peut consommer beaucoup de ressources et donc d'énergie.
En lieu et place, l'API 26 d'Android propose le concept de JobService. Un JobService permet de déclencher un traitement au bout d'un certain délai ou bien périodiquement.
Nous allons chercher à démarrer notre JobService au boot du système. Pour ce faire, nous devons commencer par définir une classe dérivant
de BroadcastReceiver
et l'inscrire dans notre fichier de manifeste. Voici une première version du code de notre BroadcastReceiver
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package com.infinisoftware.megaservice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.i("JobServiceSample", "Boot completed"); } } |
Il faut, bien entendu, enregistrer cette classe dans votre fichier de manifeste et donner les permissions requises.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.infinisoftware.megaservice"> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <receiver android:name=".BootReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> |
Déployez votre application sur périphérique Android, redémarrez-le et vérifiez que le message acquiesçant de la fin du boot est correctement produit dans le LogCat.
L'étape suivante consiste à coder notre classe de JobService. Elle doit dériver de la classe JobService
qui définit deux méthodes abstraites :
onStartJob
et onStartJob
. Dans cet exemple, nous nous contenterons de produire des messages dans la console LogCat.
Voici un exemple de code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
package com.infinisoftware.megaservice; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobService; import android.content.ComponentName; import android.content.Context; import android.util.Log; public class MainJobService extends JobService { @Override public boolean onStartJob(JobParameters jobParameters) { Log.i("JobServiceSample", "MainJobService start" ); return true; } @Override public boolean onStopJob(JobParameters jobParameters) { Log.i("JobServiceSample", "MainJobService stop" ); return true; } public static void scheduleJob(Context context) { ComponentName serviceComponent = new ComponentName(context, MainJobService.class); JobInfo jobInbo = new JobInfo.Builder(0, serviceComponent) .setMinimumLatency(1000) // Temps d'attente minimal avant déclenchement .setOverrideDeadline(3000) // Temps d'attente maximal avant déclenchement .build(); JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); jobScheduler.schedule(jobInbo); } } |
Bien entendu, un JobService doit être déclaré dans le fichier de manifeste à l'instar des activités ou des services traditionnels. Veuillez modifier ainsi votre fichier de manifeste.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.infinisoftware.megaservice"> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <receiver android:name=".BootReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> <service android:name=".MainJobService" android:permission="android.permission.BIND_JOB_SERVICE" /> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest> |
Ensuite, veuillez modifier votre classe BootReceiver
pour y planifier une exécution de votre JobService.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.infinisoftware.megaservice; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; public class BootReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.i("JobServiceSample", "Boot completed"); MainJobService.scheduleJob(context); } } |
Déployez votre application sur un périphérique Android, redémarrez ce dernier et vérifier la présence des messages dans la fenêtre LogCat.
Un JobService peut être configuré pour être invoqué périodiquement. Néanmoins la fréquence d'appel ne peut pas être trop rapide dans un souci
d'économie d'énergie. Si vous proposez une fréquence inférieure à 15 minutes, elle sera ramenée à 15 minutes.
Pour imposer une périodicité, il faut modifier le code du JobScheduler
: en voici un exemple.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package com.infinisoftware.megaservice; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobService; import android.content.ComponentName; import android.content.Context; import android.util.Log; public class MainJobService extends JobService { @Override public boolean onStartJob(JobParameters jobParameters) { Log.i("JobServiceSample", "MainJobService start" ); return true; } @Override public boolean onStopJob(JobParameters jobParameters) { Log.i("JobServiceSample", "MainJobService stop" ); return true; } public static void scheduleJob(Context context) { ComponentName serviceComponent = new ComponentName(context, MainJobService.class); JobInfo jobInbo = new JobInfo.Builder(0, serviceComponent) .setPeriodic( 1000, JobInfo.getMinFlexMillis() ) .build(); JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); jobScheduler.schedule(jobInbo); } } |
Déployez l'application sur votre périphérique Android, redémarrez ce dernier et vérifier l'apparition périodique de nouveaux messages dans la fenêtre LogCat.
Bien que votre JobScheduler ne permettent pas d'avoir une période de déclenchement inférieure à 15 minutes, il existe une solution de contournement.
L'exemple proposé (utilisé avec une API Android 26) permet un déclenchement du JobScheduler toutes les cinq secondes. Pour ce faire, nous n'allons pas utiliser
la périodicité du JobScheduler et du coup le traitement sera lancé une fois pour toute. L'astuce consiste à replanifier dans cinq secondes une nouvelle exécution
de notre JobService une fois le traitement exécuté. Voici un nouvel exemple de code pour notre classe MainJobService
permettant une exécution
périodique de notre service avec une fréquence élevée.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
package com.infinisoftware.megaservice; import android.app.job.JobInfo; import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobService; import android.content.ComponentName; import android.content.Context; import android.util.Log; public class MainJobService extends JobService { @Override public boolean onStartJob(JobParameters jobParameters) { Log.i("JobServiceSample", "MainJobService start" ); MainJobService.scheduleJob(getApplicationContext()); return true; } @Override public boolean onStopJob(JobParameters jobParameters) { Log.i("JobServiceSample", "MainJobService stop" ); return true; } public static void scheduleJob(Context context) { ComponentName serviceComponent = new ComponentName(context, MainJobService.class); JobInfo jobInbo = new JobInfo.Builder(0, serviceComponent) .setMinimumLatency(5000) // Temps d'attente minimal avant déclenchement .setOverrideDeadline(6000) // Temps d'attente maximal avant déclenchement .build(); JobScheduler jobScheduler = context.getSystemService(JobScheduler.class); jobScheduler.schedule(jobInbo); } } |
Déployez, encore une fois, l'application sur votre périphérique Android, redémarrez ce dernier et vérifier l'apparition, environ toutes les cinq secondes, de nouveaux messages dans la fenêtre LogCat.
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :