Accéder au gestionnaire de threads de l'interface utilisateur à partir d'un service

89

J'essaie quelque chose de nouveau sur Android pour lequel j'ai besoin d'accéder au gestionnaire du thread d'interface utilisateur.

Je sais ce qui suit:

  1. Le thread d'interface utilisateur a son propre gestionnaire et son propre boucleur
  2. Tout message sera placé dans la file d'attente des messages du fil d'interface utilisateur
  3. Le boucleur récupère l'événement et le transmet au gestionnaire
  4. Le gestionnaire gère le message et envoie l'événement spécifique à l'interface utilisateur

Je veux avoir mon service qui doit obtenir le gestionnaire de threads de l'interface utilisateur et mettre un message dans ce gestionnaire. Pour que ce message soit traité et envoyé à l'interface utilisateur. Ici, le service sera un service normal qui sera démarré par une application.

Je voudrais savoir si cela est possible. Si tel est le cas, veuillez suggérer des extraits de code afin que je puisse l'essayer.

Cordialement Girish

iLikeAndroid
la source

Réponses:

179

Cet extrait de code construit un gestionnaire associé au thread principal (UI): This snippet of code constructs a Handler associated with the main (UI) thread:

Handler handler = new Handler(Looper.getMainLooper());

Vous pouvez ensuite publier des éléments pour exécution dans le thread principal (UI) comme suit:

handler.post(runnable_to_call_from_main_thread);

Si le gestionnaire lui-même est créé à partir du thread principal (UI), l'argument peut être omis par souci de concision:

Handler handler = new Handler();

Le guide de développement Android sur les processus et les threads contient plus d'informations.

volée
la source
2
Je l'ai testé et cela fonctionne très bien! Un exemple de cas d'utilisation: j'ai une interface Web qui est servie par un serveur fonctionnant directement sur l'appareil. Étant donné que l'interface peut être utilisée pour interagir directement avec l'interface utilisateur et que le serveur doit fonctionner sur son propre fil, j'avais besoin d'un moyen de toucher le fil d'interface utilisateur depuis l'extérieur d'une activité. La méthode que vous avez décrite a très bien fonctionné.
mrPjer
1
Brillant. Fonctionne comme un charme, et très utile. MERCI.
JRun
parfait ^^ je l'ai juste utilisé pour mettre à jour mon interface utilisateur à partir d'un StreamingService. exactement ce dont j'avais besoin merci!
An-droid
savez-vous si je peux créer une instance singleton d'un gestionnaire et l'utiliser chaque fois que j'ai besoin d'exécuter quelque chose sur le thread d'interface utilisateur?
HelloWorld
Je suppose que nous ne saurons jamais
Denny
28

Créez un Messengerobjet attaché à votre Handleret passez-le Messengerau Service(par exemple, dans un Intentextra pour startService()). Le Servicepeut alors envoyer un Messageau Handlervia le Messenger. Voici un exemple d'application démontrant cela.

CommonsWare
la source
Merci pour cette astuce. Cela a été utile. Veuillez consulter la pile suivante pour un flux d'événements tactile vers mon activité Ligne MyDemo.dispatchTouchEvent (MotionEvent): 20 PhoneWindow $ DecorView.dispatchTouchEvent (MotionEvent) ligne: 1696 ViewRoot.handleMessage (Message) ligne: 1658 ViewRoot (Handler) .dispatchMessage (Message ) ligne: 99 Looper.loop () ligne: 123 // La gestion des événements commence ici ActivityThread.main (String []) line: 4203 Ici, ViewRoot est un gestionnaire. Je veux obtenir la référence de ce gestionnaire ... est-il possible de l'obtenir à partir de mon application?
iLikeAndroid
@iLikeAndroid: Si vous n'avez pas créé le Handler, vous ne pouvez pas y accéder, AFAIK.
CommonsWare
Je vous remercie. J'ai essayé de créer une instance de ViewRoot. Ce n'est rien d'autre qu'un gestionnaire. Je suis maintenant capable d'émettre les messages sur ce gestionnaire. Le gestionnaire reçoit le message. Mais ViewRoot n'est pas en mesure de traiter le message car il n'est pas initialisé correctement. Je dois appeler ViewRoot.setView () pour initialiser les données appropriées à ViewRoot. Je veux savoir y a-t-il une vue par défaut ou une vue de base, etc., que je peux utiliser pour initialiser?
iLikeAndroid
@iLikeAndroid: Il n'y a pas ViewRootdans le SDK Android, AFAICT.
CommonsWare
1
@ hadez30: Personnellement, je n'utilise pas beaucoup les services liés. Vous pouvez toujours utiliser un Handler/ Messenger, bien que je remplace tout cela par un bus d'événements (par exemple, l'EventBus de greenrobot).
CommonsWare
4

Pour le moment, je préfère utiliser la bibliothèque de bus d'événements comme Otto pour ce genre de problème. Abonnez-vous simplement aux composants souhaités (activité):

protected void onResume() {
    super.onResume();
    bus.register(this);
}

Ensuite, fournissez une méthode de rappel:

public void onTimeLeftEvent(TimeLeftEvent ev) {
    // process event..
}

puis lorsque votre service exécute une instruction comme celle-ci:

bus.post(new TimeLeftEvent(340));

Ce POJO sera transmis à votre activité ci-dessus et à tous les autres composants d'abonnement. Simple et élégant.

landau
la source
3

Je suggère d'essayer le code suivant:

    new Handler(Looper.getMainLooper()).post(() -> {

        //UI THREAD CODE HERE



    });
user2983041
la source
Des explications supplémentaires sont nécessaires pour aider le PO.
Moog
2

Vous pouvez obtenir des valeurs via le récepteur de diffusion ... comme suit, créez d'abord votre propre IntentFilter en tant que,

Intent intentFilter=new IntentFilter();
intentFilter.addAction("YOUR_INTENT_FILTER");

Puis créez la classe interne BroadcastReceiver comme,

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    /** Receives the broadcast that has been fired */
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getAction()=="YOUR_INTENT_FILTER"){
           //HERE YOU WILL GET VALUES FROM BROADCAST THROUGH INTENT EDIT YOUR TEXTVIEW///////////
           String receivedValue=intent.getStringExtra("KEY");
        }
    }
};

Enregistrez maintenant votre récepteur de diffusion dans onResume () en tant que,

registerReceiver(broadcastReceiver, intentFilter);

Et enfin désenregistrer BroadcastReceiver dans onDestroy () comme,

unregisterReceiver(broadcastReceiver);

Maintenant, la partie la plus importante ... Vous devez déclencher la diffusion à partir de l'endroit où vous devez envoyer des valeurs ..... alors faites comme,

Intent i=new Intent();
i.setAction("YOUR_INTENT_FILTER");
i.putExtra("KEY", "YOUR_VALUE");
sendBroadcast(i);

....à votre santé :)

Melbourne Lopes
la source
1

En kotlinthats comment vous pouvez le faire

Disons si vous souhaitez afficher le message Toast du service

val handler = Handler(Looper.getMainLooper())
handler.post {
   Toast.makeText(context, "This is my message",Toast.LENGTH_LONG).show()
}
Zohab Ali
la source
0

Solution:

  1. Créer un gestionnaire avec Looper partir du thread principal: requestHandler
  2. Créer un Handleravec à Looperpartir du thread principal: responseHandler et overridehandleMessage méthode de
  3. publier un Runnable tâche sur RequestHandler
  4. À l'intérieur Runnable tâche, appelez sendMessage sur responseHandler
  5. Ce sendMessage appel de résultat de handleMessage dans responseHandler.
  6. Obtenez des attributs de Messageet traitez-les, mettez à jour l'interface utilisateur

Exemple de code:

    /* Handler from UI Thread to send request */

    Handler requestHandler = new Handler(Looper.getMainLooper());

     /* Handler from UI Thread to process messages */

    final Handler responseHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {

            /* Processing handleMessage */

            Toast.makeText(MainActivity.this,
                    "Runnable completed with result:"+(String)msg.obj,
                    Toast.LENGTH_LONG)
                    .show();
        }
    };

    for ( int i=0; i<10; i++) {
        Runnable myRunnable = new Runnable() {
            @Override
            public void run() {
                try {
                   /* Send an Event to UI Thread through message. 
                      Add business logic and prepare message by 
                      replacing example code */

                    String text = "" + (++rId);
                    Message msg = new Message();

                    msg.obj = text.toString();
                    responseHandler.sendMessage(msg);
                    System.out.println(text.toString());

                } catch (Exception err) {
                    err.printStackTrace();
                }
            }
        };
        requestHandler.post(myRunnable);
    }
Ravindra babu
la source