[JAVA] Processus de mémorandum de programmation thread de 51 ans Handler -reference

Pour un usage personnel

Processus et threads

https://developer.android.com/guide/components/processes-and-threads?hl=JA (2019/8/24)

Le résumé multi-thread est ici

processus


Relation entre composants, processus et threads

--Lorsque l'application exécute un composant, le système Android démarre un processus Linux pour un seul thread.

--Lorsqu'un composant d'application démarre, s'il existe un autre processus, le composant démarre dans ce processus et utilise le même thread d'exécution.

  • Cependant, vous pouvez ajuster différents composants de votre application pour qu'ils s'exécutent dans différents processus, et vous pouvez créer des threads séparés pour n'importe quel processus.

Paramètres du manifeste

Si vous souhaitez gérer le processus auquel appartient un composant particulier, spécifiez-le dans le fichier manifeste.

Entrées de manifeste pour chaque type d'élément de composant (\ / \ / \ / \ ) </ strong> </ font> Prend en charge l'attribut android: process </ strong>, qui spécifie le processus qui exécute le composant.

En fonction des paramètres ** android: process **, chaque composant peut s'exécuter dans son propre processus, certains composants peuvent partager le même processus ou différents processus peuvent être utilisés. Vous pouvez également faire exécuter des composants de différentes applications dans le même processus (les applications doivent partager le même identifiant d'utilisateur Linux et être signées avec le même certificat).

L'élément ** \ ** prend également en charge ʻandroid: process` et définit les valeurs par défaut qui s'appliquent à tous les composants.

Le système Android peut arrêter les processus et détruire les composants de l'application. Lorsqu'il décide du processus à rejeter, le système détermine les candidats en fonction de la demande relative. Plus tard, j'expliquerai les règles qui déterminent le processus de rejet.


Hiérarchie d'importance des processus

Sur la base des composants en cours d'exécution dans le processus et de l'état des composants, le système Android place chaque processus dans une «hiérarchie de priorité» et tente de le supprimer des processus moins importants et de récupérer les ressources système.

Hiérarchie d'importance des processus 5 niveaux

  1. Processus de premier plan
  2. Processus visible
  3. Processus de service
  4. Processus de base
  5. Processus vide
1. Processus de premier plan
  • Hébergé par la Activity </ font> opérée par l'utilisateur ( La méthode onResume () de l'activité </ font> a été appelée)
  • Lié à l'activité gérée par l'utilisateur (hébergé par Service </ font>)
  • Service hébergé </ font> s'exécutant au premier plan (service appelé startForeground () </ font>)
  • onCreate (), onStart (), onDestroy () </ font> exécution d'un rappel de cycle de vie Service </ font> Est l'hôte de> -Hébergé la BroadcastReceiver </ font> en exécutant la méthode onReceive () </ font>

Si vous manquez de mémoire ou de ressources, vous devrez arrêter le processus de premier plan.

2. Processus visible

Un processus qui n'est pas un composant de premier plan mais qui peut affecter le contenu de la zone de notification pour l'utilisateur. Il est considéré comme un processus visible si:

  • Bien qu'il ne soit pas au premier plan, il s'agit de l'hôte de l ' Activité </ font> affichée à l'utilisateur ( onPause () </ font> > La méthode a été appelée).

  • Vous hébergez un Service </ font> lié à une activité visible (ou au premier plan).

Les processus visibles sont d'une grande importance et ne seront tués que si le processus de premier plan doit continuer à fonctionner.

3. Processus de service

Processus d'exécution d'un service démarré par la méthode startService () </ font> qui n'entre pas dans les deux catégories ci-dessus. Avec tous les processus de premier plan et visibles, ils ne sont candidats à la destruction que s'il n'y a pas assez de mémoire pour les poursuivre.

4. Processus de base

Un processus qui contient une activité qui n'est pas visible pour l'utilisateur (la méthode onStop () </ font> a été appelée). Il peut être tué à tout moment pour récupérer de la mémoire. Les services d'arrière-plan sont gérés dans la liste LRU (le moins récemment utilisé) et sont arrêtés à partir de l'opération la plus ancienne.

Si l'activité implémente exactement la méthode du cycle de vie et conserve son état actuel, elle restaure son état visuel lorsqu'elle reprend, même si elle est supprimée.

5. Processus vide

Un processus sans composants d'application actifs. Ce processus est conservé dans le but de mettre en cache le processus et lance rapidement le composant suivant. Ce processus est fréquemment tué.


Pour les composants d'importance multiple, positionnez le processus dans le rang le plus important. En outre, un processus en cours d'exécution pour un autre processus ne peut pas être positionné sous le processus dépendant / de liaison (s'il est positionné en dessous, il ne pourra pas s'exécuter si le processus dépendant est détruit. ).

Les services sont plus importants que l'arrière-plan, et pour les activités de longue durée (en particulier si le processus dure plus longtemps que l'activité), le processus doit utiliser le service plutôt que de créer un thread de travail. Par exemple, lors du téléchargement d'une image sur le Web, l'activité utilise un service qui permet à l'utilisateur de continuer à travailler en arrière-plan lorsqu'il quitte l'activité. Même les récepteurs de diffusion devraient utiliser les services plutôt que de laisser les threads les traiter pendant de longues périodes.

fil

Lorsque l'application démarre, le système crée un thread principal pour exécuter l'application. Le thread principal est très important pour être responsable de l'envoi des événements (y compris des événements de dessin) aux widgets d'interface utilisateur appropriés.

Le fil principal permet également à l'application d'interagir avec les composants de la boîte à outils de l'interface utilisateur Android (composants du package android.widget / android.view </ font>). Par conséquent, le thread principal est également appelé thread d'interface utilisateur (dans certains cas particuliers, le thread principal n'est pas le thread d'interface utilisateur).

Aucun thread distinct n'est créé pour chaque instance du composant. Tous les composants exécutés dans le même processus sont instanciés dans le thread d'interface utilisateur et le système appelle les composants envoyés par le thread d'interface utilisateur. Les méthodes qui répondent aux rappels système s'exécutent toujours dans le thread d'interface utilisateur du processus.

(Exemple: lorsque l'utilisateur touche un bouton à l'écran, le fil d'interface utilisateur de l'application envoie un événement tactile au widget, le widget définit l'état tactile et le fil d'interface utilisateur envoie une demande d'invalidation à la file d'attente d'événements. Reçoit une demande de la file d'attente et notifie le widget de dessin)

Si tout est fait dans un thread d'interface utilisateur, l'exécution d'opérations chronophages peut bloquer toute l'interface utilisateur. En outre, la boîte à outils de l'interface utilisateur Android n'est pas sûre pour les threads, vous ne pouvez donc pas interagir avec l'interface utilisateur à partir des threads de travail. Toutes les opérations doivent être effectuées à partir du thread d'interface utilisateur. Par conséquent, il existe deux règles pour le modèle de thread unique Android.

Règle de modèle de thread unique

1. Ne bloquez pas les fils de l'interface utilisateur 2. N'accédez pas à la boîte à outils de l'interface utilisateur Android à partir de quelqu'un d'autre que le thread de l'interface utilisateur </ strong> Les méthodes d'accès sont fournies à partir d'un autre thread d'interface utilisateur. ( La vue ne peut être utilisée qu'à partir du fil qui a créé la vue </ font> </ strong>)


Fil de travail

Le thread de travail est un thread qui n'a pas d'interface utilisateur et exécute la demande de traitement reçue (également appelée backgroundThread). Le thread de travail se comporte comme s'il attendait même s'il n'y avait pas de requête de traitement et l'exécute lorsqu'il reçoit la requête de traitement. Il peut y avoir plusieurs threads de travail selon le contenu de traitement, et l'emplacement peut être appelé Thread Pool. </ font>

Le modèle monothread ne doit pas bloquer les threads d'interface utilisateur pour la réactivité de l'interface utilisateur de l'application. Les opérations qui n'ont pas besoin d'être effectuées immédiatement sont exécutées dans un thread distinct (thread de travail (thread d'arrière-plan)).

<Référence: Code de violation de règle </ font> >>

python


public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            Bitmap b = loadImageFromNetwork("http://example.com/image.png ");
            mImageView.setImageBitmap(b);
        }
    }).start();
}

Toute tentative de modification de ImageView </ font> à partir du thread de travail au lieu du thread de l'interface utilisateur enfreint la règle 2 (Android UI Toolkit est accessible uniquement à partir du thread de l'interface utilisateur).

Méthode d'accès à partir d'un autre thread d'interface utilisateur
  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable,long)

<Référence: Code modifié par View.post (Runnable) </ font> >> Implémentez View.post (Runnable) pour le rendre thread-safe

python


public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            final Bitmap bitmap =
                    loadImageFromNetwork("http://example.com/image.png ");
            mImageView.post(new Runnable() {
                public void run() {
                    mImageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}

Les opérations réseau sont exécutées dans un thread distinct (loadImageFromNetwork ()). ImageView est exploité à partir du thread d'interface utilisateur (mImageView.post (new Runnable () {})).

La méthode d'utilisation de View.post pour faire fonctionner une vue à partir d'un thread d'interface utilisateur est difficile à maintenir car l'opération devient compliquée. Pour gérer les interactions complexes avec les threads de travail, vous pouvez utiliser des gestionnaires dans les threads de travail pour gérer les messages remis par les threads de l'interface utilisateur. Mais la méthode recommandée est d'étendre la classe AsyncTask. Simplifie l'exécution des tâches de thread de travail qui interagissent avec l'interface utilisateur.

Utilisation d'AsyncTask

AsyncTask effectue un traitement asynchrone. Vous n'avez pas à gérer le thread ou le gestionnaire vous-même, il bloque le fonctionnement du thread de travail et fournit le résultat au thread d'interface utilisateur.

Pour utiliser AsyncTask, sous-classez AsyncTask </ font> et exécutez-le dans un pool de threads d'arrière-plan doInBackground () </ font> callback Implémentez la méthode.

Implémentez onPostExecute () </ font> pour refléter le résultat dans l'interface utilisateur. onPostExecute () </ font> fournit le résultat de doInBackground () </ font> à l'interface utilisateur et met à jour le fil de l'interface utilisateur, donc l'interface utilisateur Peut être mis à jour en toute sécurité. Le thread d'interface utilisateur appelle execute () </ font> pour exécuter la tâche.

<Référence: réécrire le code ci-dessus avec AsycnTask>

public void onClick(View v){
   new DownloadImageTask().execute("http://example.com/image.png ");
}

private class DownloadImageTask extends AsyncTask<String,Void,Bitmap>{
   //Le système appelle la méthode suivante pour gérer dans le thread de travail,
   //   AsyncTask.execue()Livrer les paramètres passés par la méthode
   protected Bitmap doInBackground(String... urls){
      return loadImageFromNetwork(urls[0]);
   }

   //Le système appelle la méthode suivante pour le traitement dans le thread d'interface utilisateur
   //   doInBackground()Livrer le résultat à la méthode
   protected void onPostExecute(Bitmap result){
      mImageView.setImageBitmap(result);
   }
}

Le travail effectué par le thread de travail et le travail effectué par le thread d'interface utilisateur sont séparés, de sorte que l'interface utilisateur est sûre et le code est simple.

Comment fonctionne AsyncTask.

  • Les génériques peuvent être utilisés pour spécifier le type de paramètre, la valeur de progression et la valeur finale de la tâche
  • La méthode doInBackground () </ font> est automatiquement exécutée sur le thread de travail
  • onPreExecute (), onPostExecute (), onProgressUpdate () </ font> sont tous appelés dans le fil d'interface utilisateur -La valeur de retour de doInBackground () </ font> est envoyée à onPostExecute () </ font>
  • publishProgress () </ font> peut être appelé avec doInBacktround () </ font> et dans le fil d'interface utilisateur. Vous pouvez exécuter onProgressUpdate () </ font>
  • Vous pouvez annuler une tâche à tout moment à partir de n'importe quel thread
COUSION: Problèmes pouvant survenir lors de l'utilisation des threads de travail
Les modifications des paramètres d'exécution, telles que la modification de l'orientation de l'écran, peuvent entraîner le redémarrage inattendu de l'activité et détruire le thread de travail. Consultez l'exemple de code source d'application ci-dessous pour savoir comment conserver la tâche pendant le redémarrage et comment annuler correctement la tâche lorsque l'activité est détruite> https://code.google.com/archive/p/ étagères /
Pour plus d'informations sur AsyncTask> https://developer.android.com/reference/android/os/AsyncTask.html?hl=JA

Méthode sans fil

Si la méthode implémentée est appelée à partir de plusieurs threads, créez la méthode afin qu'elle soit thread-safe.

Les méthodes appelées à distance, telles que celles des services principalement liés, peuvent être appelées à partir de plusieurs threads. Lorsqu'ils sont appelés à partir de plusieurs threads, les threads qui exécutent la méthode appelée à distance sont les suivants.

-Un appel de méthode implémenté dans IBinder </ font> s'est produit dans le même processus exécutant IBinder </ font>. Si tel est le cas, le thread exécutant la méthode est le thread appelant.

  • Lorsqu'il est appelé à partir d'un autre processus, le thread qui exécute la méthode est sélectionné dans le pool de threads que le système conserve dans le même processus que IBinder </ font>. (Non exécuté dans le thread d'interface utilisateur).

Par exemple, la méthode onBind () </ font> du service est appelée à partir d'un thread d'interface utilisateur dans le processus du service, tandis que onBind () Les méthodes implémentées dans l'objet retourné par </ font> (telles que la sous-classe qui implémente la méthode RPC) sont appelées à partir des threads du pool de threads. Étant donné que le service peut avoir plusieurs clients, plusieurs pools de threads peuvent exécuter la même méthode IBinder </ font> en même temps, la méthode IBinder doit donc être implémentée pour être thread-safe. ..

Méthode RPC: la méthode est appelée localement, exécutée à distance (dans un autre processus) et le résultat est renvoyé à l'appelant. </ font>

De même, les fournisseurs de contenu peuvent recevoir des demandes de données envoyées par d'autres processus. Les classes ContentResolver </ font> et ContentProvider </ font> obscurcissent la façon dont la communication inter-processus est gérée. Cependant, la méthode ContentProvider </ font> ( query (), insert (), delete (), updater (), getType) répond à ces demandes. () </ Font>) est appelé le pool de threads dans le processus du fournisseur de contenu, pas le thread d'interface utilisateur du processus. Ces méthodes peuvent être appelées par plusieurs méthodes en même temps et doivent être implémentées pour être thread-safe.

Fil sûr

Contrôlez (synchronisation), arbitrez ou contrôlez d'autres threads pendant qu'un thread est en cours d'exécution pour éviter les conflits de threads (relations où plusieurs threads accèdent à la même classe, méthode ou variable en même temps et détruisent les données). Contrôle exclusif) et sécurisez la classe ou la méthode même si l'accès simultané se produit à partir de plusieurs threads.

Communication interprocessus (IPC)

Android dispose d'un mécanisme de communication inter-processus à l'aide d'un appel de procédure à distance (RPC), dans lequel une méthode est appelée à partir d'une activité ou d'un autre composant d'application, puis exécutée à distance (un autre processus), et le résultat est appelé. Retourne à. Puisque le système est responsable du traitement de la communication inter-processus, le développeur doit uniquement définir et implémenter l'interface de programmation RPC. L'exécution IPC nécessite que l'application soit liée au service à l'aide de bindService () </ font>. Pour plus d'informations> https://developer.android.com/guide/components/services.html?hl=JA

Handle Vous pouvez utiliser Handler pour créer Message </ font> et des objets exécutables (tels que Runnable) associés au MessageQueue </ font> du thread. Vous pouvez l'envoyer et le traiter.

Une instance de Handler est associée à un seul thread et à la file d'attente de messages de ce thread.

Lorsque vous créez un nouveau gestionnaire, le gestionnaire est affecté au thread généré et à sa file d'attente de messages. Le gestionnaire pourra alors remettre le message et la file d'attente de messages exécutables, et pourra le supprimer de la file d'attente et exécuter le message.

Utilisation du gestionnaire principal

--Planifiez un message et rendez-le exécutable à l'heure prévue --Queue le message à exécuter dans un thread séparé

Programmer un message

Méthode à utiliser

Post Version post(Runnable)/postAtTime(java.lang.Runnable,long)/postDelayed(Runnable,Object,long)

Send Message Version sendEmptyMessege(int)/sendMessage(Message)/sendMessageAtTime(Message,long)/sendmessageDelayed(Message,long)

La version Post fonctionne en mettant un objet en file d'attente lorsqu'il reçoit un objet exécutable.

La version SendMassege met les objets de message en file d'attente. L'objet message contient une collection de données qui seront utilisées dans la méthode HandleMessage (Message) du gestionnaire. (HandleMessage (Massage) implémente une sous-classe de Handler)

Publier ou envoyer un gestionnaire vous permettra de le traiter dès que la file d'attente des messages sera prête. Vous pouvez également spécifier le report du traitement et l'heure d'exécution.

Plus tard, j'expliquerai la mise en œuvre des opérations de chronométrage telles que le traitement du délai d'attente et les ticks (de type minuterie).

Lorsque l'application s'exécute, le thread principal devient un thread dédié pour exécuter la file d'attente de messages. Les files d'attente de messages gèrent les objets d'application de niveau supérieur tels que les activités, les diffusions et les récepteurs, ainsi que les fenêtres qu'ils créent.

Vous pouvez utiliser Handler pour créer un autre thread pour communiquer avec l'application principale. Pour ce faire, utilisez les méthodes post et sendMessage à l'avance.

Les fichiers exécutables et les messages sont planifiés dans la file d'attente de messages du gestionnaire et exécutés au moment approprié.


Résumé du gestionnaire

■ ** Extension de classe Thread ** | ** Héritage d'interface exécutable ** ** Communication avec le fil de l'interface utilisateur **   view#post(Runnable)   View#postDelayed(Runnable.long)   runOnUiThread(Runnable) ■ Classe ** AsyncTask ** ** Traitement et communication ** Avec execute (), doInBackground (), postExecute (), etc. ■ Classe ** Handler ** ** Communication entre multithreads ** Responsable de la communication entre les threads créés séparément   Message   Runnable

Récapitulatif du gestionnaire

class android.os.Handler

Le gestionnaire permet la communication entre les threads d'interface utilisateur et les threads de travail!

MessageQueue </ font> </ strong> (empile et stocke les messages et les exécutables) et récupère les messages et les exécutables de MessageQueue dans le fil de discussion qui a créé l'instance du gestionnaire > Générez Looper </ font> </ strong>. Handler réalise la communication entre les threads, il semble donc qu'il soit conçu pour pouvoir être référencé par d'autres threads </ font>.

Une instance de Handler est associée au thread et à la file d'attente de messages générés et renvoie true lorsqu'un message, etc. est ajouté à la file d'attente. Si Looper se termine avant que le temps spécifié ne s'écoule, Runnable sera abandonné et deviendra False, donc si False apparaît, la file d'attente des messages peut être terminée!

Communication avec le gestionnaire

  • Comment utiliser Message
  • Comment utiliser Runnable

Le gestionnaire fournit un moyen de communiquer des informations entre les threads qui ont déjà été créés. Il réalise la communication entre les threads de travail ainsi que les threads de l'interface utilisateur.

Si vous utilisez Message, Looper traite handleMessage et si vous utilisez Runnable, Runnable est exécuté du côté de l'interface utilisateur.

###### Cas 1 du gestionnaire et du message

Message


public class MainActivity extends AppCompatActivity {

    TextView textView;

    //Fil UI(MainActivity)Créer une instance Handler avec
    //Désormais, MessageQueue et Looper sont des threads d'interface utilisateur.
    //Étant donné que le gestionnaire d'instance peut être référencé à partir d'un autre thread
    //Si vous faites référence au gestionnaire dans un autre fil et postez un message
    //Fil d'exécution de l'interface utilisateur (MainActivity)Et Looper s'en chargera pour vous!
    //C'est cool! !!
    //Alors, venez au gestionnaire!
    Handler handler = new MyHandler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView1);

        MyLovelyMessage();
    }

    private void MyLovelyMessage() {
        //Fil d'exécution de l'interface utilisateur (MainActivity)Accéder au gestionnaire généré dans
        //Mettre Message dans le gestionnaire et sendMessage!
        //Le thread Looper de l'interface utilisateur devrait maintenant fonctionner très bien!
        Message message = handler.obtainMessage(1);
        handler.sendMessage(message);
    }

    class MyHandler extends Handler{
        @Override
        public void handleMessage(Message message){
            //Définit ce que fait la poignée dans le thread de l'interface utilisateur
            //Il sera exécuté automatiquement par Looper!
            String msg = String.valueOf(message.what);
            textView.setText(msg);
        }
    }
}

Simplifiez le code ci-dessus en tant que fonction interne

Message


public class MainActivity extends AppCompatActivity {

    TextView textView;

    Handler handler = new Handler(){
      @Override
      public void handleMessage(Message message){
          String msg = String.valueOf(message.what);
          textView.setText(msg);
      }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView1);

        MyLovelyRunnable();
    }

    private void MyLovelyRunnable() {
        Message message = handler.obtainMessage(1);
        handler.sendMessage(message);
    }
}
Exemple utilisant Handler et Runnable
public class MainActivity extends AppCompatActivity {

    TextView textView;

    //Gestionnaire de thread d'interface utilisateur(MainAvtivity.class)Générer avec
    //Maintenant, MessageQueue et Looper sont dans ce fil(Fil UI)Associé à
    //
    //Étant donné que l'instance Handler peut également être référencée à partir d'autres threads
    //Dans un autre thread, en utilisant le gestionnaire d'instances
    //Exécutable dans ce fil(Fil UI)Si vous le donnez à
    //Ce fil(Côté filetage UI)Sera capable d'exécuter Runnable
    //C'est bien!
    //Créons maintenant une instance de Handler! !!
    Handler handler = new Handler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.textView1);

        MyLovelyHandler();

    }

    private void MyLovelyHandler(){
        new Thread(new Runnable() {
            @Override
            public void run() {
                //Fil UI(MainActivity)L'instance Handler créée dans
                //Peut être référencé à partir d'autres threads
                //Mettre Runnable sur le gestionnaire d'instance
                //Fil d'exécution de l'interface utilisateur (MainActivity)Remettons-le et faisons-le traiter!
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        textView.setText("I Love Handler");
                    }
                });
            }
        }).start();
    }
}


Gestionnaire et service: un exemple de division d'un fichier en plusieurs parties

Cible min: api28 ou supérieur.

Fichier créé

  • MainActivity.java
    Créer une instance de gestionnaire
    Exécuter le service de premier plan
  • MyService.java
    Créer un message et envoyer un message
  • MyHandler sendMessage Traite le message envoyé
Lorsque j'appelle Service avec Foreground, il plante à moins que je ne démarreForeground (int, notification) côté Service dans les 5 secondes, mais comme le code est long, je ne le décrirai pas cette fois et il plantera, désolé.

MainActivity.java


public class MainActivity extends AppCompatActivity {
    public static TextView textView;

    //Créez un objet Handler et faites de MessageQueue et Looper un thread d'interface utilisateur!
    //Cela stockera le message envoyé à l'objet Handle dans MessageQueue
    //Looper le sortira et le traitera! (Le contenu du processus est MyHandler.Définissez-le en classe! )
    static MyHandler myHandler = new MyHandler();

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

        textView = findViewById(R.id.textView);

        //Créez un objet Intent et appelez Service!
        Intent intent = new Intent(this,MyService.class);
        startForegroundService(intent);

    }
}

MyService.java


public class MyService extends Service {
    public MyService() {
    }

    @Override
    public int onStartCommand(Intent intent,int flags,int startid){
        //SendMessage en mettant un message sur l'objet Handle de MainActivity!!
        //La destination est MessageQueue du thread d'interface utilisateur!
        //Après cela, Looper est MyHandler.Exécuter la classe et la traiter!!
        Message message = MainActivity.myHandler.obtainMessage(1);
        MainActivity.myHandler.sendMessage(message);
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

MyHandler.java


//Message stocké dans MessageQueue du thread d'interface utilisateur
//Le thread Looper de l'interface utilisateur a la poignée suivante()Sera exécuté!

public class MyHandler extends Handler {
    @Override
    public void handleMessage(Message message){
        String msg = String.valueOf(message.what);
        MainActivity.textView.setText(msg);
    }
}
Exemple de code pour le cas du minuteur dans Handler Runnable Service

J'ai été autorisé à faire référence. Merci beaucoup. Ici Mais je n'ai pas compris pourquoi `myHandler.postDelayed (runnnable, milli) a été appelé deux fois.

<Aperçu> Il ne traite pas les notifications, donc il se bloque immédiatement. Accédez automatiquement au service Foreground lorsque l'application est lancée ForegroundService, traitement du minuteur avec handler.postDeployed (runnnable, mill) Reprendre le comptage avec le bouton de démarrage Arrêtez de compter avec le bouton d'arrêt

MainActivity.java


public class MainActivity extends AppCompatActivity {
    public static TextView textView;
    static Handler myHandler = new Handler();
    boolean signal =false;

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

        Button btnStart= findViewById(R.id.btnStart);
        Button btnStop = findViewById(R.id.btnStop);
        textView = findViewById(R.id.textView);

        final Intent intent = new Intent(this,MyService.class);
        startForegroundService(intent);
        
        btnStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(signal == false){
                    MyService.runnable.run();
                    signal = true;
                }
            }
        });

        btnStop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                myHandler.removeCallbacks(MyService.runnable);
                signal = false;
            }
        });
    }
}

MyService.java


public class MyService extends Service {
    static Runnable runnable;
    public MyService() {
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startid) {

        runnable = new Runnable() {
            int i=0;
            @Override
            public void run() {
                i++;
                String st = String.valueOf(i);
                MainActivity.textView.setText(st);
                MainActivity.myHandler.postDelayed(this,1000);
            }
        };
        
//        MainActivity.myHandler.postDelayed(runnable,1000);
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

Recommended Posts