Was genau macht die Post-Methode?

106

Ich bin auf eine sehr seltsame Funktion gestoßen.

Wenn ich versuche, eine Animation im Hauptthread auszuführen, wird sie nicht gestartet. Wenn ich die Animation mit ausführe

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

Es fängt an.

Ich habe die CurrentThreadvor dem Start der Animation gedruckt und beide drucken main.

Offensichtlich fehlt mir hier etwas, da beide die Animation im Haupt-Thread starten sollten ... Ich vermute, dass der Beitrag die Aufgabe zur Warteschlange hinzufügt und zu einem "korrekteren Zeitpunkt" beginnt, aber ich würde es gerne wissen Was passiert hier in größerer Tiefe.

EDIT: Lassen Sie mich die Dinge klären - meine Frage ist, warum das Starten der Animation auf Post dazu führt, dass sie gestartet wird, wenn das Starten der Animation auf dem Haupt-Thread dies nicht tut.

Gal
quelle
Ist dieses Verhalten spezifisch für eine Android-Version? Ich konnte es auf Android 4.1.2 nicht reproduzieren!
Akdeniz
Ich habe dieses Verhalten auf Android 2.3.3 reproduziert. Aber für AnimationDrawable! Die normale AnimationInstanz wurde bei jedem Setup erfolgreich animiert. Für den AnimationDrawableFall; Wenn Sie versuchen, es zu starten onCreate, wird es nicht gestartet, da es in diesem Moment nicht an die Anzeige angehängt ist. Es ist also kein Threading-Problem für AnimationDrawable. Vielleicht gilt das Gleiche für Animation? developer.android.com/guide/topics/graphics/…
Akdeniz

Antworten:

161

post : post bewirkt, dass Runnable zur Nachrichtenwarteschlange hinzugefügt wird.

Runnable: Stellt einen Befehl dar, der ausgeführt werden kann. Wird häufig verwendet, um Code in einem anderen Thread auszuführen.

run () : Startet die Ausführung des aktiven Teils des Klassencodes. Diese Methode wird aufgerufen, wenn ein Thread gestartet wird, der mit einer Klasse erstellt wurde, die Runnable implementiert.

getView().post(new Runnable() {

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

Code :getView().startAnimation(a);

in Ihrem Code,

post bewirkt, dass Runnable (der Code wird in einem anderen Thread ausgeführt) die Nachrichtenwarteschlange hinzufügt.

StartAnimation wird also in einem neuen Thread ausgelöst, wenn es aus der messageQueue abgerufen wird

[EDIT 1]

Warum verwenden wir einen neuen Thread anstelle des UI-Threads (Haupt-Thread)?

UI-Thread:

  • Wenn die Anwendung gestartet wird, wird der UI-Thread automatisch erstellt

  • Es ist für den Versand der Ereignisse an die entsprechenden Widgets verantwortlich, einschließlich der Zeichenereignisse.

  • Es ist auch der Thread, mit dem Sie mit Android-Widgets interagieren

Wenn Sie beispielsweise die Schaltfläche a auf dem Bildschirm berühren, sendet der UI-Thread das Berührungsereignis an das Widget, das seinerseits den Status "gedrückt" festlegt und eine ungültige Anforderung an die Ereigniswarteschlange sendet. Der UI-Thread stellt die Anforderung in die Warteschlange und benachrichtigt das Widget, sich selbst neu zu zeichnen.

Was passiert, wenn ein Benutzer eine Taste drückt, die longOperation ausführt?

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

Die Benutzeroberfläche friert ein. Das Programm kann sogar abstürzen.

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

Es verstößt gegen die Android-Regel, die die Benutzeroberfläche niemals direkt vom Arbeitsthread aus aktualisiert

Android bietet verschiedene Möglichkeiten, um von anderen Threads aus auf den UI-Thread zuzugreifen.

  • Activity.runOnUiThread (ausführbar)
  • View.post (Runnable)
  • View.postDelayed (lauffähig, lang)
  • Handler

Wie unten,

View.post (Runnable)

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();
}

Handler

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();                
}

Geben Sie hier die Bildbeschreibung ein

Für mehr Information

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
quelle
15
Warum unterscheidet sich das Starten der Animation im Post-Modus vom Ausführen im Haupt-Thread, wenn beide letztendlich im selben Thread ausgeführt werden?
Gal
Weil dieses Single-Thread-Modell in Android-Anwendungen zu einer schlechten Leistung führen kann.
Talha
1
Was hat schlechte Leistung damit zu tun, dass keine Animation angezeigt wird?
Gal
17
Ich denke nicht, dass dies die Frage beantwortet, dies ist eher eine generische Antwort für Anfänger, die nichts über den UI-Thread und das Multithreading wissen. Dies erklärt nicht, warum die Animation funktioniert, wenn die Animation in die Warteschlange gestellt wird. Eine Animation soll direkt im UI-Thread ausgeführt werden, ohne dass Tricks von post () oder runOnUiThread () verwendet werden müssen.
Carrizo
3
Alle UI-Arbeiten sollten sich im Hauptthread (UI-Thread) befinden. Der Trick, mit dem die Animation funktioniert, indem post () verwendet wird, anstatt den Hauptthread sofort aufzurufen, ist Zeit: Wenn Sie sofort im Hauptthread aufrufen, bedeutet dies, dass Sie "Animation jetzt starten" gesagt haben. Zu diesem Zeitpunkt ist die Ansicht möglicherweise noch nicht fertig zur Animation (messen, zeichnen ...). Wenn Sie dies jedoch in post () einfügen, wird startAnimation irgendwann in der Warteschlange anstehen, um die Ansicht für die Animation vorzubereiten.
NguyenDat
35

Wird dies in onCreate oder onCreateView durchgeführt? In diesem Fall befindet sich die App möglicherweise nicht in einem Zustand, in dem die Ansicht an das Fenster angehängt ist. Viele auf View-Metriken basierende Algorithmen funktionieren möglicherweise nicht, da Dinge wie die Messungen und die Position der View möglicherweise nicht berechnet wurden. Für Android-Animationen müssen sie normalerweise die UI-Mathematik durchlaufen

View.post stellt die Animation tatsächlich in die Nachrichtenschleife der Ansicht ein. Sobald die Ansicht an das Fenster angehängt wird, wird die Animation ausgeführt, anstatt sie manuell ausführen zu lassen.

Sie führen tatsächlich Dinge auf dem UI-Thread aus, jedoch zu einem anderen Zeitpunkt

Joe Plante
quelle
Die akzeptierte Antwort ist irreführend, wenn auf dem Poster angegeben ist, dass "Post den Runnable verursacht (der Code wird in einem anderen Thread ausgeführt)". Dies ist die richtige Antwort "Sie führen tatsächlich Dinge auf dem UI-Thread aus, aber zu einer anderen Zeit" - plus 1
smitty1
18

Werfen Sie einen Blick hier für eine gute Antwort. view.post () ist so ziemlich dasselbe wie handler.post (). Es wird in die Haupt-Thread-Warteschlange gestellt und ausgeführt, nachdem die anderen ausstehenden Aufgaben abgeschlossen sind. Wenn Sie activity.runOnUiThread () aufrufen, wird es sofort im UI-Thread aufgerufen.

Tas Morf
quelle
31
Ein massiver (und äußerst hilfreicher) Unterschied, den ich festgestellt habe, ist die Ausführung in view.post (), die aufgerufen wird, wenn die Ansicht zum ersten Mal angezeigt wird. IE, Sie können festlegen, dass beim Aufblasen der Ansicht eine Animation gestartet wird, um sie dann zu einem späteren Zeitpunkt der Ansichtshierarchie hinzuzufügen. An diesem Punkt wird die Animation ausgeführt und Sie müssen sich keine Sorgen mehr machen.
DeeV
Tatsächlich veröffentlicht handler.post () nicht immer eine Nachricht / ausführbare Datei im Hauptthread. Dies hängt davon ab, wie der Handler erstellt wird (er kann einem Looper in einem anderen Thread zugeordnet werden). Auf der anderen Seite läuft view.post () immer auf dem Haupt-Thread
Yair Kukielka
4

Ich denke, das Problem könnte die Lebenszyklusmethode sein, bei der Sie die post () -Methode aufrufen. Machst du es in onCreate ()? Wenn ja, schauen Sie sich an, was ich in der onResume () -Dokumentation der Aktivität gefunden habe:

auf Wiederaufnahme()

In API-Ebene 1 hinzugefügt void onResume () Wird nach onRestoreInstanceState (Bundle), onRestart () oder onPause () aufgerufen, damit Ihre Aktivität mit dem Benutzer interagiert. Dies ist ein guter Ort, um Animationen zu starten , Geräte mit exklusivem Zugriff (wie die Kamera) zu öffnen usw.

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

Wie Joe Plante sagte, ist die Ansicht möglicherweise nicht bereit, Animationen zu starten, sobald Sie post () aufrufen. Verschieben Sie sie daher nach onResume ().

PD: Wenn Sie den Code nach onResume () verschieben, können Sie den Aufruf von post () entfernen, da Sie sich bereits im UI-Thread befinden und die Ansicht zum Starten von Animationen bereit sein sollte.

Carrizo
quelle
3
onResumekann mehrmals aufgerufen werden (Bildschirme werden in den Ruhezustand versetzt, Aktivität in den Backstack verschoben usw.), nachdem "die Ansicht fertig ist". Wenn von aufgerufen onResume, wird möglicherweise eine Flagge benötigt, um das Wetter zu verfolgen, bei dem die Animation bereits gestartet wurde, um ein mehrmaliges (erneutes) Starten zu vermeiden.
Samis