Introduction à cette section

Très bien, dans les trois sections précédentes, nous avons étudié et appris Activity sous Android. Je crois que tout le monde en a beaucoup profité ! Dans cette section, nous continuons à découvrir le deuxième composant d'Android : Service. Bon, sans plus attendre, commençons le contenu de cette section !


1. Concepts liés aux threads

Avant de commencer à apprendre Service, comprenons d'abord quelques notions sur les threads !

1) Concepts associés :

  • Programme: Un ensemble d'instructions écrites dans un certain langage pour accomplir une tâche spécifique (un ensemble decode statique)
  • Processus:Un programme en cours d'exécution, une unité d'ordonnancement du système et d'allocation des ressourcesunité indépendante, le système d'exploitation alloue un segment d'espace mémoire à chaque processus ! L'exécution dynamique séquentielle d'un programme est le processus complet de chargement, d'exécution et d'achèvement du code !
  • Thread: Une unité d'exécution plus petite qu'un processus. Chaque processus peut avoir plusieurs threads,Threaddoit être placé dans unprocessuspour pouvoir s'exécuter,Les threads sont gérés par le programme, tandis queles processus sont ordonnancés par le système!
  • Comprendre le multithreading:ParallèleExécuter plusieurs instructions, et distribuerdes tranches de temps CPUà chaque thread selon l'algorithme d'ordonnancement. En fait, c'estle partage de tempsd'exécution, mais le temps de commutation est très court, donc les utilisateurs ont simplement l'impression que c'est « simultané » !

2) Cycle de vie d'un thread :

3) Trois façons de créer des threads :

  1. Hériter de la classe Thread
  2. Implémenter l'interface Runnable
  3. Implémenter l'interface CallableSi : en utilisant le thread créé dans 2, vous pouvez le démarrer directement comme ceci :
    new Thread(myThread).start();
    
    Le plus souvent, nous préférons utiliser des classes anonymes, c'est-à-dire le style suivant :
    new Thread(new Runnable(){
         public void run();
             }).start();

2. Différences entre Service et Thread

En fait, il n'y a pas beaucoup de relation entre eux, mais beaucoup d'amis confondent souvent les deux ! Thread est un thread, la plus petite unité d'exécution de programme, et l'unité de base pour l'allocation CPU ! Service, en revanche, est un composant fourni par Android qui permet une résidence à long terme en arrière-plan. L'utilisation la plus courante consiste à effectuer des opérations de polling ! Ou si vous voulez faire quelque chose en arrière-plan, comme des téléchargements de mises à jour en arrière-plan ! Rappelez-vous de ne pas confondre ces deux concepts !


3. Diagramme du cycle de vie de Service


4. Analyse du cycle de vie

D'accord, à partir du diagramme du cycle de vie ci-dessus, nous pouvons voir qu'il y a deux façons d'utiliser Service dans Android :

1)StartService() pour démarrer le Service
2)BindService() pour démarrer le Service
PS : Il existe une autre façon, qui consiste à lier le Service après l'avoir démarré !


1) Explication détaillée des méthodes associées :

  • onCreate()onCreate() : lorsque le Service est créé pour la première fois, cette méthode est appelée en retour immédiatement ; cette méthode ne sera appelée qu'une seule fois durant tout le cycle de vie !
  • onDestory()onDestroy() : lorsque le Service est fermé, cette méthode est appelée en retour ; cette méthode ne sera appelée en retour qu'une seule fois !
  • onStartCommand(intent,flag,startId)onStartCommand() : dans les versions antérieures, il s'agissait de onStart(intent, startId). Lorsqu'un client appelle la méthode startService(Intent), elle sera appelée en retour. La méthode StartService peut être appelée plusieurs fois, mais aucun nouvel objet Service ne sera créé ; à la place, l'objet Service précédemment créé est réutilisé, mais la méthode onStartCommand() continuera d'être appelée en retour !
  • IBinder onOnbind(intent)onBind() : cette méthode est une méthode que tous les Services doivent implémenter. Cette méthode renvoie un objet IBinder, par l'intermédiaire duquel l'application communique avec le composant Service !
  • onUnbind(intent)onUnbind() : cette méthode est appelée en retour lorsque tous les clients liés au Service sont déconnectés !

2) StartService pour démarrer un Service

①Lors du premier démarrage, une instance de Service sera créée, et les méthodes onCreate() et onStartCommand() seront appelées successivement. À ce stade, le Service entre dans l'état d'exécution. Si StartService est appelé à nouveau pour démarrer le Service, aucun nouvel objet Service ne sera créé. Le système réutilisera directement l'objet Service précédemment créé et appellera sa méthode onStartCommand() !
②Mais un tel Service n'a pas de lien nécessaire avec son appelant. C'est-à-dire que lorsque l'appelant termine son propre cycle de vie, tant que stopService n'est pas appelé, le Service continuera de s'exécuter !
③Peu importe combien de fois le Service a été démarré, il vous suffit d'appeler StopService une seule fois pour arrêter le Service.


3) BindService pour démarrer un Service

①Lorsqu'un Service est lié avec bindService pour la première fois, le système instanciera une instance de Service et appellera ses méthodes onCreate() et onBind(). Ensuite, l'appelant peut interagir avec le Service via IBinder. Par la suite, si bindService est de nouveau utilisé pour lier le Service, le système ne créera pas de nouvelle instance de Service et ne rappellera pas non plus la méthode onBind() ; il transmettra simplement directement l'objet IBinder aux autres clients ajoutés par la suite !
②Si nous voulons nous délier du service, il nous suffit d'appeler unbindService(). À ce moment-là, les méthodes onUnbind et onDestroy seront appelées ! C'est le cas pour un seul client. Si plusieurs clients se lient au même Service, la situation est la suivante : après qu'un client a terminé son interaction avec le service, il appelle la méthode unbindService() pour se délier. Lorsque tous les clients se sont déliés du service, le système détruira le service. (Sauf si le service a également été démarré par la méthode startService().)
③De plus, contrairement à la situation ci-dessus, le Service en mode bindService est interdépendant de l'appelant. On peut le comprendre comme « des sauterelles attachées à la même corde » — si l'une meurt, toutes meurent ensemble. Après bindService, une fois que l'appelant est détruit, le Service se terminera également immédiatement !
Analyse de Context.bindService() lors de l'invocation d'un Service via BindServicebindService(Intent Service,ServiceConnection conn,int flags)
service: Le Service à démarrer est spécifié via cet intent.
conn: objet ServiceConnection, utilisé pour surveiller la connexion entre le client et le Service. Lors d'une connexion réussie, la méthode onServiceConnected(ComponentName, IBinder) de cet objet est appelée en retour ; si l'hôte où réside le Service se termine en raison d'un arrêt anormal ou d'autres raisons, entraînant la déconnexion entre le Service et le client, appelezonServiceDisconnected(ComponentName) ne sera pas appelée si l'on se déconnecte activement viaunBindService()!
flags:Spécifie s'il faut créer automatiquement le Service lors de la liaison (si le Service n'a pas été créé). Le paramètre peut être 0 (ne pas créer automatiquement) ou BIND_AUTO_CREATE (créer automatiquement).4) Liaison via bindService après avoir démarré le Service avec StartService


4) Liaison via bindService après avoir démarré le Service avec StartService

La condition préalable est : la méthode onUnbind() renvoie true !!!
onCreate( )->onStartCommand( )->onBind( )->onUnbind( )->onRebind( )
PS :Ici, certains lecteurs peuvent être confus. Après avoir appelé unbindService, le Service ne devrait-il pas appeler la méthode onDestroy() ? En fait, c'est parce que ce Service a été démarré par notre StartService, donc si vous appelez la méthode onUnbind() pour le délier, le Service ne se terminera pas non plus !
Conclusion :Si nous utilisons bindService pour lier un Service démarré, notez qu'il s'agit d'un Service déjà démarré !!! Le système transmet uniquement l'objet IBinder interne du Service à l'Activity, et ne lie pas le cycle de vie du Service à l'Activity. Par conséquent, lors de l'appel à unBindService() pour le délier, le Service ne sera pas détruit !


5. Vérification du cycle de vie

Ensuite, écrivons du code pour vérifier le cycle de vie :

1) Vérifier l'ordre d'appel du démarrage du Service avec StartService

Tout d'abord, nous définissons un Service personnalisé, redéfinissons les méthodes pertinentes et imprimons la vérification dans logcat :

TestService1.java

public class TestService1 extends Service {  
    private final String TAG = "TestService1";    
    //必须要实现的方法  
    @Override  
    public IBinder onBind(Intent intent) {  
        Log.i(TAG, "onBind方法被调用!");  
        return null;  
    }  
  
    //Service被创建时调用  
    @Override  
    public void onCreate() {  
        Log.i(TAG, "onCreate方法被调用!");  
        super.onCreate();  
    }  
      
    //Service被启动时调用  
    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.i(TAG, "onStartCommand方法被调用!");  
        return super.onStartCommand(intent, flags, startId);  
    }  
      
    //Service被关闭之前回调  
    @Override  
    public void onDestroy() {  
        Log.i(TAG, "onDestory方法被调用!");  
        super.onDestroy();  
    }  
}

Enregistrement complet du Service dans AndroidManifest.xml

<!-- 配置Service组件,同时配置一个action -->  
<service android:name=".TestService1">  
            <intent-filter>  
                <action android:name="com.jay.example.service.TEST_SERVICE1"/>  
            </intent-filter>  
</service>  

Ensuite, un simple fichier de mise en page avec deux boutons, et enfin la MainActivity. Dans les événements de clic des boutons, appelez respectivement startService() et stopService() !

public class MainActivity extends Activity {  
  
    private Button start;  
    private Button stop;  
      
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
          
        start = (Button) findViewById(R.id.btnstart);  
        stop = (Button) findViewById(R.id.btnstop);  
        //创建启动Service的Intent,以及Intent属性  
        final Intent intent = new Intent();  
        intent.setAction("com.jay.example.service.TEST_SERVICE1");  
        //为两个按钮设置点击事件,分别是启动与停止service  
        start.setOnClickListener(new OnClickListener() {              
            @Override  
            public void onClick(View v) {  
                startService(intent);                 
            }  
        });  
          
        stop.setOnClickListener(new OnClickListener() {           
            @Override  
            public void onClick(View v) {  
                stopService(intent);  
                  
            }  
        });  
    }  
}

Capture d'écran de l'exécution :

Cliquez pour démarrer le service :

N'ayant rien de mieux à faire, cliquez encore quelques fois :

Enfin, cliquez pour arrêter le service :

Analyse des résultats :

D'après les résultats d'exécution ci-dessus, nous pouvons vérifier ce qui a été expliqué dans notre diagramme de cycle de vie : Nous constatons que la méthode onBind() n'est pas appelée. De plus, cliquer pour démarrer le Service plusieurs fois ne fera qu'appeler de manière répétée la méthode onStartCommand ! Peu importe combien de fois nous démarrons le Service, un seul stopService arrêtera le Service !


2) Vérifier l'ordre de démarrage du Service avec BindService :

Avant de commencer à écrire du code, nous devons d'abord comprendre quelques points : D'abord, la méthode bindService de Context donnée sous le premier grand diagramme :

  • Objet ServiceConnection : surveille la connexion entre le visiteur et le Service. Si la connexion réussit, onServiceConnected() est appelé en retour. Si la connexion est interrompue anormalement ou pour d'autres raisons, provoquant la déconnexion du Service du visiteur, la méthode onServiceDisconnected est appelée en retour. Appeler unBindService() n'appellera pas cette méthode !
  • Dans la méthode onServiceConnected, il y a un objet IBinder, qui peut être utilisé pour communiquer avec le Service lié ! Lorsque nous développons une classe Service, nous devons implémenter la méthode IBinder onBind() par défaut. L'objet IBinder retourné par cette méthode est passé au paramètre de onServiceConnected dans l'objet ServiceConnection. Ici, nous pouvons communiquer avec le Service via cet IBinder !

Résumé :
Étape 1 :Dans le Service personnalisé, héritez de Binder pour implémenter votre propre objet IBinder.
Étape 2 :Retournez votre propre objet IBinder via la méthode onBind().
Étape 3 :Dans la classe qui lie le Service, définissez un objet ServiceConnection et redéfinissez deux méthodes, onServiceConnected et onDisconnected ! Ensuite, lisez directement les paramètres passés par l'IBinder !

Très bien, nous allons maintenant écrire du code pour vérifier. Ici, nous définissons un Service de chronométrage, puis nous démontrons l'utilisation de BindService et le déroulement des appels de méthodes ! Le code est relativement simple, donc nous ne l'expliquerons pas.

TestService2.java :

public class TestService2 extends Service {  
    private final String TAG = "TestService2";  
    private int count;  
    private boolean quit;  
      
    //定义onBinder方法所返回的对象  
    private MyBinder binder = new MyBinder();  
    public class MyBinder extends Binder  
    {  
        public int getCount()  
        {  
            return count;  
        }  
    }  
      
    //必须实现的方法,绑定改Service时回调该方法  
    @Override  
    public IBinder onBind(Intent intent) {  
        Log.i(TAG, "onBind方法被调用!");  
        return binder;  
    }  
  
    //Service被创建时回调  
    @Override  
    public void onCreate() {  
        super.onCreate();  
        Log.i(TAG, "onCreate方法被调用!");  
        //创建一个线程动态地修改count的值  
        new Thread()  
        {  
            public void run()   
            {  
                while(!quit)  
                {  
                    try  
                    {  
                        Thread.sleep(1000);  
                    }catch(InterruptedException e){e.printStackTrace();}  
                    count++;  
                }  
            };  
        }.start();  
          
    }  
      
    //Service断开连接时回调  
    @Override  
    public boolean onUnbind(Intent intent) {  
        Log.i(TAG, "onUnbind方法被调用!");  
        return true;  
    }  
      
    //Service被关闭前回调  
    @Override  
    public void onDestroy() {  
        super.onDestroy();  
        this.quit = true;  
        Log.i(TAG, "onDestroyed方法被调用!");  
    }  
      
    @Override  
    public void onRebind(Intent intent) {  
        Log.i(TAG, "onRebind方法被调用!");  
        super.onRebind(intent);  
    }  
} 

Enregistrez le composant Service dans AndroidManifest.xml :

<service android:name=".TestService2" android:exported="false">  
        <intent-filter>  
            <action android:name="com.jay.example.service.TEST_SERVICE2"/>  
        </intent-filter>  
</service>  

MainActivity.java :

public class MainActivity extends Activity {  
  
    private Button btnbind;  
    private Button btncancel;  
    private Button btnstatus;  
      
    //保持所启动的Service的IBinder对象,同时定义一个ServiceConnection对象  
    TestService2.MyBinder binder;  
    private ServiceConnection conn = new ServiceConnection() {  
          
        //Activity与Service断开连接时回调该方法  
        @Override  
        public void onServiceDisconnected(ComponentName name) {  
            System.out.println("------Service DisConnected-------");  
        }  
          
        //Activity与Service连接成功时回调该方法  
        @Override  
        public void onServiceConnected(ComponentName name, IBinder service) {  
            System.out.println("------Service Connected-------");  
            binder = (TestService2.MyBinder) service;  
        }  
    };  
      
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        btnbind = (Button) findViewById(R.id.btnbind);  
        btncancel = (Button) findViewById(R.id.btncancel);  
        btnstatus  = (Button) findViewById(R.id.btnstatus);  
        final Intent intent = new Intent();  
        intent.setAction("com.jay.example.service.TEST_SERVICE2");  
        btnbind.setOnClickListener(new OnClickListener() {            
            @Override  
            public void onClick(View v) {  
                //绑定service  
                bindService(intent, conn, Service.BIND_AUTO_CREATE);                  
            }  
        });  
          
        btncancel.setOnClickListener(new OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                //解除service绑定  
                unbindService(conn);                  
            }  
        });  
          
        btnstatus.setOnClickListener(new OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                Toast.makeText(getApplicationContext(), "Service的count的值为:"  
                        + binder.getCount(), Toast.LENGTH_SHORT).show();  
            }  
        });  
    }  
}  

Capture d'écran de l'exécution :

Cliquez pour lier le Service :

Continuez à cliquer sur lier : aucun changement.

Obtenez l'état actuel du Service :

Délier :

Si nous fermons directement l'Activity après la liaison, cela signalera une erreur, puis appellera automatiquement les méthodes onUnbind et onDestroy !

。。。

D'après les résultats d'exécution ci-dessus, cela vérifie ce qui suit dans le diagramme du cycle de vie :

En utilisant BindService pour lier un Service, les méthodes onCreate() et onBind() sont appelées dans l'ordre. Nous pouvons retourner un objet IBinder personnalisé dans la méthode onBind(). Ensuite, la méthode onServiceConnected() de ServiceConnection est appelée, dans laquelle l'objet IBinder peut être obtenu pour effectuer les opérations associées. Lorsque le Service est délié, les méthodes onUnbind et onDestroy sont automatiquement appelées. Bien sûr, dans le cas de plusieurs clients connectés, il est nécessaire de tous les délier avant que la méthode onDestroy ne soit appelée pour la destruction !