Participer au site avec un Tip
Rechercher
 

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 :

Mise en oeuvre d'un JobService

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.

Démarrage du JobService au boot du périphérique

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

}
Définition d'un BroadcastReceiver

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>
Inscription du BroadcastReceiver dans le fichier de manifeste

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.

Lancement, une unique fois, de notre JobService

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

}
Le code de notre classe de JobService

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>
Inscription du JobService dans le fichier de manifeste

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éfinition d'un BroadcastReceiver

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.

Lancement périodique de notre JobService

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

}
Modification du JobScheduler pour avoir une périodicité
bien que la fréquence spécifiée soit d'une seconde, votre JobService sera déclenché toutes les 15 minutes.

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.

Solution de contournement pour avoir une période de déclenchement inférieure à 15 minutes

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

}
Modification du JobScheduler pour une replanification périodique du service

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.