[AdSense-A]

Nous allons apprendre à travers ce tutoriel Android comment utiliser un « service » qui tourne en tache de fond.

Pour illustrer ce concept mal connu et un peu mystérieux, nous allons créer une petite application pour faire une blague à vos amis.

Fonctionnalité de l’application.

Tutoriel Android Tutoriel Android

Le principe est simple, cette application remplace l’écran d’accueil du smartphone par une image qui montre l’écran cassé.

Lors du lancement, l’application vous demande de valider la blague en appuyant sur le bouton central. Une fois l’action effectuée, l’application se ferme et plus rien ne se passe jusqu’à la prochaine  utilisation du smartphone par votre ami. Il découvrira alors son écran d’accueil avec une fausse image de l’écran cassé.

Pas de stress, une fois que votre ami déverrouillera son smartphone, la fausse image disparaîtra.

Un service, c’est quoi ?

Un service est un composant qui fonctionne en tache de fond. Comme pour une activité, un service possède un cycle de vie (OnCreate(), OnStart(Intent i) ou OnStartCommand(Intent i, int flags, int startId) et OnDestroy()). La grande différence avec une activité est qu’il ne possède pas d’interface graphique. L’utilisateur ne peut donc pas interagir avec lui.

Pourquoi utiliser un service dans ce tutoriel Android ?

Dans certain cas, il est utile de créer un programme qui fonctionne en tache de fond sans que l’application soit visible, comme par exemple un lecteur audio (qui fonctionne sans l’application ouverte) ou un système de minutage qui effectue des actions à intervalle régulier.

Dans notre exemple, nous devons utiliser un service pour que l’utilisateur ne voit pas notre application en fonctionnement. L’affichage de l’image de l’écran cassé doit se faire par un programme qui n’a pas d’interface graphique et qui n’est donc pas visible par l’utilisateur. Pour ne pas que notre ami ne soupçonne la blague, le service va ajouter l’image lorsque le smartphone passe en veille. Pour cela nous allons utiliser dans le service un « BroadcastReceiver » qui nous permettra de détecter la mise en veille du smartphone ainsi que son déverrouillage (pour supprimer l’image et détruire le service).

Déroulement de l’application.

L’application visible.

  1. Lancement de l’application.
  2. Appuie sur le bouton central.
  3. Lancement du service.
  4. Fermeture de l’application.

Le service.

  1. Détection de la mise en veille du smartphone.
  2. Ajout de l’image de l’écran cassé.
  3. Lorsque l’utilisateur appui sur le bouton pour sortir le smartphone de la veille, l’image de l’écran cassé est alors visible.
  4. Détection du déverrouillage du smartphone par l’utilisateur.
  5. Suppression de l’image et destruction du service.

Création de l’application.

Note : pour suivre ce tutoriel, nous considérons que vous savez créer une nouvelle application Android avec Android Studio.

  1. Créez une nouvelle application avec Android Studio en choisissant l’option « Blank Activity ».
  2. Nommer l’application (« MyJokeApplication ») dans notre cas.
  3. Une fois le projet crée par Android studio, ouvrez le fichier MainActivity.java et ajouter le code suivant :
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button_close_app_and_go_joke = (Button) findViewById(R.id.button_close_app_and_go_joke);
        button_close_app_and_go_joke.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {

                Intent service = new Intent(getApplicationContext(), JokeService.class);
                startService(service); // Lancement du service
                finish(); // Fermeture de l'application

            }
        });
    }
}

 

L’activité principale est très simple. Après avoir chargé l’interface graphique (R.layout.activity_main), nous affectons un « listener » au bouton central afin de détecter le clic par l’utilisateur.

Lors d’un clic sur le bouton, l’application lance le service (par un Intent et startService) et se ferme automatiquement (finish()). Il y a donc plus que le service qui « tourne » en tache de fond.

Le fichier graphique activity_main.xml est lui aussi très simple :

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="fr.webdream.myjokeapplication.MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/fermerapplication"
        android:id="@+id/button_close_app_and_go_joke"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true" />
</RelativeLayout>

Pour fonctionner, le service doit être dans un premier temps déclaré dans le fichier manifest de l’application.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="fr.webdream.myjokeapplication">

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service
            android:name=".JokeService"
            android:enabled="true"
            android:exported="true"></service>

    </application>

</manifest>

Une fois le service lancé et pour détecter la mise en veille et le déverrouillage du smartphone nous allons initialiser le « BroadcastReceiver » dans le « onStartCommand » du service.

public int onStartCommand (Intent intent, int flags, int startId){
      try {
        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF); //détection de la mise en veille du smartphone
        filter.addAction(Intent.ACTION_USER_PRESENT); //détection du déverrouillage du smartphone
        BroadcastReceiver mReceiver = new receiverScreen();
        registerReceiver(mReceiver, filter);
    } catch (Exception e) {

    }
    return START_STICKY;
}

public class receiverScreen extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)){
            Show_joke(); //Affichage de l'image de l'écran cassé
        }
        if (intent.getAction().equals(Intent.ACTION_USER_PRESENT)){
            hide_broken_screen_and_destroy_service(); //On supprime l'image et on détruit le service.
        }
    }

}

Pour afficher / supprimer l’image nous utilisons le « windowManager », qui permet d’ajouter une vue à l’écran du smartphone.

private void Show_joke(){

    if (showing){return;} //Si l'image est déjà affichée, on sort.

    windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    
    broken_screen_View = new ImageView(this); //Création de l'image de l'écran cassé
    Bitmap bmp_broken_screen;
    if(getResources().getDisplayMetrics().widthPixels>getResources().getDisplayMetrics().heightPixels) //On choisi l'image en fonction du mode portrait ou en paysage
    {
        bmp_broken_screen = BitmapFactory.decodeResource(getResources(), R.drawable.broken_screen_land);
    }
    else
    {
        bmp_broken_screen = BitmapFactory.decodeResource(getResources(), R.drawable.broken_screen);
    }
    broken_screen_View.setImageBitmap(bmp_broken_screen);
    broken_screen_View.setScaleType(ImageView.ScaleType.FIT_XY);

    //Définition des paramètres a appliquer à l'image
    final WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED|WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);

    //Affichage de l'image
    windowManager.addView(broken_screen_View, params);
    showing=true;
}

private void hide_broken_screen_and_destroy_service(){
    windowManager.removeViewImmediate(broken_screen_View);
    showing=false;
    stopSelf();
}

Ce qui nous donne le service complet suivant :

package fr.webdream.myjokeapplication;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.IBinder;
import android.graphics.PixelFormat;
import android.util.Log;
import android.view.WindowManager;
import android.widget.ImageView;

public class JokeService extends Service {

    private WindowManager windowManager;
    private ImageView broken_screen_View;
    private Boolean showing;

    public JokeService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return null;
    }

    public int onStartCommand (Intent intent, int flags, int startId){

          try {
            IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);

            filter.addAction(Intent.ACTION_SCREEN_OFF);
            filter.addAction(Intent.ACTION_USER_PRESENT);

            BroadcastReceiver mReceiver = new receiverScreen();

            registerReceiver(mReceiver, filter);
        } catch (Exception e) {

        }

        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        showing=false;
    }

    @Override
    public void onDestroy(){
        Log.v("SERVICE", "Service killed");
        if (showing){
            windowManager.removeViewImmediate(broken_screen_View);
            showing=false;
        }
        super.onDestroy();
    }

    private void Show_joke(){

        if (showing){return;} //Si l'image est déjà affichée, on sort.

        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

        broken_screen_View = new ImageView(this); //Création de l'image de l'écran cassé
        Bitmap bmp_broken_screen;
        if(getResources().getDisplayMetrics().widthPixels>getResources().getDisplayMetrics().heightPixels) //On choisi l'image en fonction du mode portrait ou en paysage
        {
            bmp_broken_screen = BitmapFactory.decodeResource(getResources(), R.drawable.broken_screen_land);
        }
        else
        {
            bmp_broken_screen = BitmapFactory.decodeResource(getResources(), R.drawable.broken_screen);
        }
        broken_screen_View.setImageBitmap(bmp_broken_screen);
        broken_screen_View.setScaleType(ImageView.ScaleType.FIT_XY);

        //Définition des paramètres a appliquer à l'image
        final WindowManager.LayoutParams params = new WindowManager.LayoutParams(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY, WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED|WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN|WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT);

        //Affichage de l'image
        windowManager.addView(broken_screen_View, params);
        showing=true;
    }

    private void hide_broken_screen_and_destroy_service(){
        windowManager.removeViewImmediate(broken_screen_View);
        showing=false;
        stopSelf();
    }

    public class receiverScreen extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {

            if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)){
                Show_joke(); //Affichage de l'image de l'écran cassé
            }
            if (intent.getAction().equals(Intent.ACTION_USER_PRESENT)){
                hide_broken_screen_and_destroy_service(); //On supprime l'image et on détruit le service.
            }
        }

    }

}

En conclusion, l’utilisation de service dans une application Android reste simple et peut être très utile dans des applications ou des actions en tache de fond doivent être effectuées.

Si vous désirez reproduire cette application, vous trouverez ici les deux fichiers images (portrait et paysage) utilisés :

Image portrait

Image paysage

 

5461 View