Service avancé


Introduction à cette section

Dans la section précédente, nous avons découvert le cycle de vie du Service et les deux méthodes de démarrage du Service. Cette section continue d'avoir une compréhension approfondie d'IntentService dans Service et des exemples d'utilisation de Service : Implémentation des services de premier plan et sondage !


1. Utilisation d'IntentService

Après la section précédente, nous savons déjà comment définir et démarrer le service, mais si nous le faisons directement Placez le thread qui prend beaucoup de temps dans la méthode onStart() du service. Bien que vous puissiez le faire, c'est très simple. Cela provoquera une exception ANR (Application Not Responding) et le responsable Android sera introduit. Le service a le passage suivant : Traduction directe :

1. Le service n'est pas un processus distinct, il est dans le même processus que son application
2. un fil de discussion, ce qui signifie que nous devons éviter d'effectuer des opérations fastidieuses dans Service

Ainsi, Android nous propose des alternatives pour résoudre les problèmes ci-dessus, qui sont discutés ci-dessousIntentService ; IntentService est une classe qui hérite de Service et gère les requêtes asynchrones. Un thread de travail gère les opérations fastidieuses et l'enregistrement d'intention demandé sera ajouté à la file d'attente

Workflow :

Le client démarre IntentService via startService (Intention) ; Nous n'avons pas besoin de contrôler manuellement l'IntentService. Lorsque la tâche est terminée, l'IntentService s'arrêtera automatiquement ; IntentService peut être démarré plusieurs fois et chaque opération fastidieuse sera traitée dans IntentService sous la forme d'une file d'attente de travail. Exécuté dans la méthode de rappel onHandleIntent, et un seul thread de travail sera exécuté à la fois. Après l'exécution d'un, puis de deux

Ensuite, il y a une démonstration de code sur Internet. comparer Service et IntentService, Définissez un temps de sommeil suffisamment long, démontrez l'exception ANR du service, puis montrez à quel point l'IntentService est bon ! Je ne démontrerai pas le Service ici. Ceux sur Internet sont tous des Services personnalisés, puis dans la méthode onStart(). Thread.sleep(20000) déclenche alors une exception ANR. Si vous êtes intéressé, vous pouvez essayer d'écrire le code vous-même. Ici, je démontre uniquement l'utilisation d'IntentService !

TestService3.java

public class TestService3 extends IntentService {
private final String TAG = "hehe";
//Le constructeur de la classe parent doit être implémenté
public TestService3()
{
super("TestService3");
}

//Méthodes de base qui doivent être remplacées
@Override
protected void onHandleIntent(Intent intent) {
//L'intention est à partir de Activity Sent, portant des paramètres d'identification, effectuez différentes tâches en fonction de différents paramètres
String action = intent.getExtras().getString("param" ​​
if(action.equals("s1")); Log.i(TAG,"Démarrer le service1");
else if(action.equals("s2"))Log.i(TAG,"Démarrer le service2"); s3" ))Log.i(TAG,"Start service3"); }catch(InterruptedException e } 🎜> Log.i(TAG,"onBind");
return super.onBind(intent);
}

@Override
public void onCreate() {
Log. i(TAG,"onCreate");
super.onCreate();    @Override  
    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.i(TAG,"onStartCommand");  
        return super.onStartCommand(intent, flags, startId);  
    }  
  
  
    @Override  
    public void setIntentRedelivery(boolean enabled) {  
        super.setIntentRedelivery(enabled);  
        Log.i(TAG,"setIntentRedelivery");  
    }  
      
    @Override  
    public void onDestroy() {  
        Log.i(TAG,"onDestroy");  
        super.onDestroy();  
    }  
      
}

AndroidManifest.xml注册下Service

<service android:name=". TestService3" android :exported="false">  
    <intent-filter >  
        <action android:name="com.test.intentservice"/>  
    </intent-filter>  
</service>

在MainActivity启动三次服务:

la classe publique MainActivity étend l'activité {  
  
    @Override    
    protégé void on Create(Bundle SaveInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
         
        Intent it1 = new Intent("com.test.intentservice");  
        Bundle b1 = new Bundle();  
        b1.putString("param", "s1");  
        it1.putExtras(b1);  
         
        Intent it2 = new Intent("com.test.intentservice");  
        Bundle b2 = new Bundle();  
        b2.putString("param", "s2");  
        it2.putExtras(b2);  
         
        Intent it3 = new Intent("com.test.intentservice");  
        Bundle b3 = new Bundle();  
        b3.putString("param", "s3");  
        it3.putExtras(b3);  
         
        //接着启动多次IntentService,每次启动,都会新建一个工作线程   
        //但始终只有一个IntentService实例  
        startService(it1);  
        startService(it2);  
        startService(it3);  
    }  
}

Exécuter la capture d'écran :

1.jpg

Résumé :

Lorsqu'une tâche en arrière-plan doit être divisée en plusieurs sous-tâches, puis exécutée en séquence, les sous-tâches (En termes simples, il s'agit d'une opération asynchrone). À ce stade, si nous définissons toujours un service normal, puis Il est très fastidieux de créer un thread dans la méthode onStart puis de contrôler le thread ; À ce stade, vous devez personnaliser un IntentService et effectuer les tâches associées dans la méthode onHandleIntent() !


2. L'activité communique avec le service

Nos opérations précédentes consistent à démarrer et arrêter le service via l'activité si nous démarrons un téléchargement. Le Service en arrière-plan, et nous voulons connaître la progression de la tâche de téléchargement dans le Service ! Alors cela nécessite définitivement un service Communiquez avec Activity, et le moyen de communication entre eux est la méthode onBind() dans Service ! Renvoyez un objet Binder personnalisé !

Le processus de base est le suivant :

  • 1. Dans le service personnalisé, personnalisez une classe Binder, puis écrivez toutes les méthodes qui doivent être exposées dans cette classe !
  • 2. Dans la classe Service, instanciez cette classe Binder personnalisée, puis remplacez la méthode onBind() pour renvoyer cet objet Binder !
  • 3. Instanciez un objet ServiceConnection dans la classe Activity, remplacez la méthode onServiceConnected(), puis Récupérez l’objet Binder puis appelez la méthode appropriée !

3. Implémentation d'un service frontal simple

Après avoir appris maintenant, nous savons tous que le service s'exécute généralement plus tard, mais la priorité système du service Elle est encore relativement faible. Lorsque la mémoire système est insuffisante, il est possible de recycler le Service exécuté en arrière-plan. Dans cette situation, nous pouvons utiliser un service de premier plan pour rendre le service légèrement moins susceptible d'être tué par le système. Bien sûr, il est toujours possible d'être tué... Le service dit de premier plan est la notification affichée dans la barre d'état !

C'est également très simple à mettre en œuvre. Le projet sur lequel j'ai récemment travaillé utilisait ce service front-end, j'ai donc extrait le code principal. Partager :

Dans la classe Service personnalisée, réécrivez onCreate(), puis personnalisez la notification en fonction de vos propres besoins ; Après la personnalisation, appelez simplement startForeground(1, notification object) ! Le code de base est le suivant :

public void onCreate()
{
    super.onCreate();
    Notification.Builder localBuilder = new Notification.Builder(this);
    localBuilder.setContentIntent(PendingIntent.getActivity (this, 0, new Intent(this, MainActivity.class), 0));
    localBuilder.setAutoCancel(false);
    localBuilder.setSmallIcon(R.mipmap.ic_cow_icon);
    localBuilder.setTicker( "Foreground Service Start");
    localBuilder.setContentTitle("Socket服务端");
    localBuilder.setContentText("正在运行...");
    startForeground(1, localBuilder.getNotification()) ;
}

Capture d'écran de l'effet en cours d'exécution :

2.png


4. Implémentation d'un fil d'arrière-plan planifié simple

En plus des services frontaux mentionnés ci-dessus, il existe une autre utilisation courante du service dans le développement réel, qui consiste à exécuter des tâches planifiées. Par exemple, interroger signifie demander au serveur de temps en temps de confirmer l'état du client ou de mettre à jour les informations. attendez! Il existe deux méthodes de chronométrage qui nous sont proposées dans Android, utilisant la classe Timer et le mécanisme d'alarme !

Le premier ne convient pas aux tâches planifiées qui doivent s'exécuter en arrière-plan pendant une longue période. Une fois le processeur mis en veille, les tâches planifiées dans Timer. Il ne peut pas fonctionner ; l'alarme n'a pas cette situation. Elle a pour fonction de réveiller le CPU. De plus, le CPU doit également être distingué. Réveillez-vous avec le réveil de l'écran !

Processus d'utilisation :

  • Étape 1 : Obtenir le service :Gestionnaire d'AlarmManager = (AlarmManager) getSystemService( ALARM_SERVICE);
  • Étape 2 : Définissez la tâche planifiée via la méthode setint anHour = 2 * 1000 ; long triggerAtTime = SystemClock.elapsedRealtime() + anHour ; manager.set(AlarmManager.RTC_WAKEUP,triggerAtTime,endingIntent);
  • Étape 3 : Définir un service et ouvrez un fil de transaction dans onStartCommand pour traiter une logique de synchronisation
  • Étape 4 : Définissez une diffusion pour démarrer le serviceEnfin, n'oubliez pas d'enregistrer le service et Boradcast dans AndroidManifest.xml !

Détails du paramètre : set (type int, long startTime,PendingIntent pi)

①type : a cinq valeurs facultatives :
AlarmManager.ELAPSED_REALTIME :Le réveil n'est pas disponible lorsque le téléphone est en veille. Dans cet état, le réveil utilise l'heure relative (par rapport au début. du démarrage du système), état La valeur est 3;
AlarmManager.ELAPSED_REALTIME_WAKEUPLe réveil réveillera le système et exécutera des fonctions d'invite en état de veille. Dans cet état, le réveil utilise également des fonctions relatives. heure et la valeur d'état est 2 ;
AlarmManager .RTCLe réveil n'est pas disponible en état de veille. Dans cet état, le réveil utilise l'heure absolue, c'est-à-dire l'heure actuelle du système. , et la valeur d'état est 1 ;
AlarmManager.RTC_WAKEUP signifie que le réveil sera en état de veille. Réveillez le système et exécutez la fonction d'invite. Dans cet état, le réveil utilise. l'heure absolue et la valeur d'état est 0 ;
AlarmManager.POWER_OFF_WAKEUP signifie que le réveil peut également exécuter la fonction d'invite normalement lorsque le téléphone est éteint, il y en a donc 5 parmi les plus couramment utilisés. les États, Dans cet état, le réveil utilise également l'heure absolue, et la valeur de l'état est 4 cependant, cet état semble être affecté par la version du SDK, et certaines versions ne le supportent pas

PS ; : Le premier paramètre détermine le deuxième paramètre Type, s'il est REALTIME, utilisez : La méthode SystemClock.elapsedRealtime() peut obtenir le nombre de millisecondes écoulées depuis le démarrage du système. S'il s'agit de RTC, utilisez : System.currentTimeMillis() pour obtenir l'heure de 1970.1.1 0 à Nombre de millisecondes écoulées maintenant

②startTime : L'heure de première exécution du réveil, en millisecondes, l'heure peut être personnalisée, mais l'heure actuelle est généralement utilisée. Il est à noter que cet attribut est étroitement lié au premier attribut (type) du réveil correspondant au premier paramètre. Le temps relatif est utilisé (ELAPSED_REALTIME et ELAPSED_REALTIME_WAKEUP), alors cette propriété Vous devez utiliser l'heure relative (par rapport à l'heure de démarrage du système). Par exemple, l'heure actuelle est exprimée comme suit : SystemClock.elapsedRealtime(); Si le réveil correspondant au premier paramètre utilise le temps absolu (RTC, RTC_WAKEUP, POWER_OFF_WAKEUP), alors cet attribut doit utiliser le temps absolu. Par exemple, l'heure actuelle est exprimée sous la forme : System.currentTimeMillis().

③PendingIntent :Lie l'action d'exécution du réveil, comme l'envoi d'une diffusion, l'envoi d'un rappel, etc. En attenteIntention C'est la classe d'encapsulation d'Intent.
Il est à noter que si le rappel d'alarme est mis en œuvre au démarrage du service, Pour obtenir l'objet PendingIntent, Pending.getService doit être utilisé (Contexte c, int i, Intent intent, int j) méthode ;
Si le rappel d'alarme est mis en œuvre par diffusion, Pour obtenir l'objet PendingIntent, PendingIntent.getBroadcast doit être utilisé Méthode (Context c, int i, Intent intent, int j) ;
Si la méthode Activity est utilisée pour implémenter l'invite de réveil, l'acquisition de l'objet PendingIntent Vous devez utiliser PendingIntent.getActivity(Context c,int i,Intent intent,int j) méthode.
Si ces trois méthodes ne sont pas utilisées correctement, même si aucune erreur ne sera signalée, l'effet d'invite d'alarme ne sera pas visible.

De plus :

À partir de la version 4.4 (API 19), l'heure de déclenchement de la tâche Alarme peut devenir imprécise et être retardée Oui système. Pour optimiser la consommation d'énergie, si vous avez besoin de précision, vous pouvez appeler la méthode setExtra()~

Code principal :

classe publique LongRunningService étend le service {
@Override
public IBinder onBind (intention d'intention) {
return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
​ //Ouvrez un fil de discussion ici pour effectuer des opérations logiques spécifiques : public void run() {
Log.d("BackService", new Date() .toString());
}
}).start();
AlarmManager manager = (Alarm Manager) getSystemService(ALARM_SERVICE);
// L'heure définie ici est de. imprimez toutes les deux secondes =-=, changez-le vous-même
int anHour = 2 * 1000;
long triggerAtTime = SystemClock (elapsedRealtime() + anHour;
Intent i = new Intent(this,AlarmReceiver.class) ;
PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
manager.set(AlarmManager. ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
return super.onStartCommand(intent, flags, startId) ;
anteceiver.java



classe publique AlarmReceiver étend BroadcastReceiver {
@Override

public void onReceive(Context context, Intent intent) { Intent i = new Intent(context,LongRunningService.class) ; context.startService(i);

}
}


Résumé de cette section :

Dans cette section, nous continuons à en apprendre davantage sur Service, IntentService et Service Deux cas courants dans le développement actuel : implémentation du service front-end et arrière-plan du service La réalisation de Service! Dans la section suivante, nous continuerons à étudier l'AIDL du service et la communication inter-processus. Restez à l'écoute ~


Référence : "La première ligne de code Android" - Guo Lin : Un très bon livre d'introduction à Android !