Que fait exactement la méthode de publication?

106

J'ai rencontré une fonctionnalité très étrange.

Lorsque j'essaye d'exécuter une animation sur le fil principal, cela ne démarre pas. Quand je lance cette animation en utilisant

getView().post(new Runnable() {
            @Override
            public void run() {
                getView().startAnimation(a);
            }
        });

Ça commence.

J'ai imprimé le CurrentThreadavant de commencer l'animation et les deux imprimer main.

De toute évidence, il me manque quelque chose ici, car les deux devraient démarrer l'animation sur le fil principal ... Je suppose que lorsque la publication ajoute la tâche à la file d'attente, elle commence à un "moment plus correct", mais j'aimerais savoir ce qui se passe ici plus en profondeur.

EDIT: Permettez-moi de clarifier les choses - ma question est, pourquoi le démarrage de l'animation sur la publication le fait démarrer, lorsque le démarrage de l'animation sur le fil principal ne le fait pas.

Fille
la source
Ce comportement est-il spécifique à une version Android? Je n'ai pas pu le reproduire sur Android 4.1.2!
Akdeniz
J'ai reproduit ce comportement sur Android 2.3.3. Mais pour AnimationDrawable! L' Animationinstance ordinaire a commencé à s'animer avec succès sur chaque configuration. Au AnimationDrawablecas où; lorsque vous essayez de le démarrer onCreate, il ne démarre pas car il n'est pas attaché à la vue à ce moment. Ce n'est donc pas un problème de filetage pour AnimationDrawable. Peut-être que la même chose s'applique Animation? developer.android.com/guide/topics/graphics/…
Akdeniz

Réponses:

161

post : post entraîne l'ajout du Runnable à la file d'attente des messages,

Exécutable: représente une commande qui peut être exécutée. Souvent utilisé pour exécuter du code dans un thread différent.

run () : Démarre l'exécution de la partie active du code de la classe. Cette méthode est appelée lorsqu'un thread est démarré qui a été créé avec une classe qui implémente Runnable.

getView().post(new Runnable() {

         @Override
         public void run() {
             getView().startAnimation(a);
         }
     });

code :getView().startAnimation(a);

dans votre code,

post provoque le Runnable (le code sera exécuté dans un thread différent) pour ajouter la file d'attente de messages.

Donc startAnimation sera déclenché dans un nouveau thread lorsqu'il est extrait de messageQueue

[EDIT 1]

Pourquoi utilisons-nous un nouveau thread au lieu du thread UI (thread principal)?

Fil de discussion de l'interface utilisateur:

  • Lorsque l'application est lancée, Ui Thread est créé automatiquement

  • il est chargé de distribuer les événements aux widgets appropriés et cela inclut les événements de dessin.

  • C'est aussi le fil avec lequel vous interagissez avec les widgets Android

Par exemple, si vous touchez le bouton a à l'écran, le thread d'interface utilisateur distribue l'événement tactile au widget qui à son tour définit son état enfoncé et publie une demande d'invalidation dans la file d'attente d'événements. Le thread d'interface utilisateur supprime la demande et notifie au widget de se redessiner.

Que se passe-t-il si un utilisateur appuie sur un bouton qui fera une longue opération?

((Button)findViewById(R.id.Button1)).setOnClickListener(           
             new OnClickListener() {        
        @Override
    public void onClick(View v) {
            final Bitmap b = loadImageFromNetwork();
            mImageView.setImageBitmap(b);
}
});

L'interface utilisateur se fige. Le programme peut même planter.

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
        final Bitmap b = loadImageFromNetwork();
        mImageView.setImageBitmap(b);
    }
  }).start();
}

Il enfreint la règle Android qui ne met jamais à jour l'interface utilisateur directement à partir du thread de travail

Android propose plusieurs façons d'accéder au thread d'interface utilisateur à partir d'autres threads.

  • Activity.runOnUiThread (exécutable)
  • View.post (Exécutable)
  • View.postDelayed (Exécutable, long)
  • Gestionnaire

Comme ci-dessous,

View.post (Exécutable)

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
      final Bitmap b = loadImageFromNetwork();
      mImageView.post(new Runnable() {
        public void run() {
          mImageView.setImageBitmap(b);
        }
      });
    }
  }).start();
}

Gestionnaire

final Handler myHandler = new Handler(Looper.getMainLooper());

(new Thread(new Runnable() {

    @Override
    public void run() {
       final Bitmap b = loadImageFromNetwork();
      myHandler.post(new Runnable() {                           

        @Override
        public void run() {
           mImageView.setImageBitmap(b);
          }
        });
      }
    })).start();                
}

entrez la description de l'image ici

Pour plus d'informations

http://android-developers.blogspot.com/2009/05/painless-threading.html

http://www.aviyehuda.com/blog/2010/12/20/android-multithreading-in-a-ui-environment/

Talha
la source
15
Alors, pourquoi le démarrage de l'animation sur la publication est-il différent de son exécution sur le fil principal, alors qu'ils finissent tous les deux sur le même fil?
Gal
Parce que ce modèle à un seul thread peut donner de mauvaises performances dans les applications Android.
Talha
1
Qu'est-ce qu'une mauvaise performance a à voir avec le fait de ne pas montrer une animation?
Gal
17
Je ne pense pas que cela réponde à la question, c'est plutôt une réponse générique pour les débutants qui ne savent rien de l'interface utilisateur et du multi threading. Cela n'explique pas pourquoi lancer l'animation dans la file d'attente fait fonctionner l'animation; une animation est censée être quelque chose à exécuter directement dans le thread d'interface utilisateur sans utiliser d'astuces post () ou runOnUiThread ().
carrizo
3
Tout le travail de l'interface utilisateur doit être sur le thread principal (thread UI). L'astuce qui permet à l'animation de fonctionner en utilisant post () au lieu d'appeler immédiatement dans le fil principal est Time: si vous appelez tout de suite dans le fil principal, cela signifie que vous avez dit "démarrer l'animation maintenant", mais pour le moment, la vue n'est peut-être pas prête pour l'animation (mesurer, dessiner ...). Mais si vous mettez cela dans post (), il sera parfois en attente de startAnimation dans la file d'attente pour préparer la vue prête pour l'animation.
NguyenDat
35

Cela se fait-il sur onCreate ou onCreateView? Si tel est le cas, l'application peut ne pas être dans un état où la vue est attachée à la fenêtre. De nombreux algorithmes basés sur les métriques View peuvent ne pas fonctionner car des éléments tels que les mesures et la position de View peuvent ne pas avoir été calculés. Les animations Android nécessitent généralement qu'elles s'exécutent via les mathématiques de l'interface utilisateur

View.post met en fait l'animation en file d'attente sur la boucle de message de la vue, donc une fois que la vue est attachée à la fenêtre, elle exécute l'animation au lieu de l'exécuter manuellement.

Vous exécutez en fait des choses sur le thread de l'interface utilisateur, mais à un moment différent

Joe Plante
la source
La réponse acceptée est trompeuse lorsque l'affiche déclare "la publication provoque le Runnable (le code sera exécuté dans un thread différent)". C'est la bonne réponse "Vous exécutez en fait des choses sur le fil d'interface utilisateur, mais à un moment différent" - plus 1
smitty1
18

Jetez un œil ici pour une bonne réponse. view.post () est à peu près identique à handler.post (). Il entre dans la file d'attente du thread principal et est exécuté une fois les autres tâches en attente terminées. Si vous appelez activity.runOnUiThread (), il sera appelé immédiatement sur le thread de l'interface utilisateur.

Tas Morf
la source
31
Une différence massive (et extrêmement utile) que j'ai trouvée est que l'exécutable de view.post () sera appelé lorsque la vue est affichée pour la première fois. IE, vous pouvez le configurer pour démarrer une animation lors du gonflage de la vue, puis à un moment donné dans le futur, l'ajouter enfin à la hiérarchie de la vue. À ce stade, l'animation s'exécutera et vous n'aurez pas à vous en soucier.
DeeV
En fait, handler.post () ne publie pas toujours un message / exécutable sur le thread principal. Cela dépend de la façon dont le gestionnaire est créé (il peut être associé à un Looper sur un thread différent). D'autre part, view.post () fonctionnera toujours sur le fil principal
Yair Kukielka
4

Je pense que le problème pourrait être la méthode du cycle de vie où vous appelez la méthode post (). Le faites-vous dans onCreate ()? si c'est le cas, regardez ce que j'ai trouvé dans la documentation onResume () de l'activité:

pour résumer()

Ajouté dans l'API niveau 1 void onResume () Appelé après onRestoreInstanceState (Bundle), onRestart () ou onPause (), pour que votre activité commence à interagir avec l'utilisateur. C'est un bon endroit pour commencer les animations , ouvrir des appareils à accès exclusif (comme la caméra), etc.

https://developer.android.com/reference/android/app/Activity.html#onResume ()

Ainsi, comme l'a dit Joe Plante, la vue n'est peut-être pas prête à démarrer les animations au moment où vous appelez post (), alors essayez de la déplacer vers onResume ().

PD: En fait, si vous déplacez le code vers onResume (), je pense que vous pouvez supprimer l'appel post () puisque vous êtes déjà dans le fil d'interface utilisateur et que la vue devrait être prête à démarrer les animations.

Carrizo
la source
3
onResumepeut être appelé plusieurs fois (les écrans se mettent en veille, l'activité repoussée en arrière, etc ...) après avoir été initialement lorsque "la vue est prête". S'il est appelé depuis onResume, un drapeau peut être nécessaire pour suivre la météo pendant laquelle l'animation a déjà été lancée, pour éviter de (re) démarrer plusieurs fois.
samis