Cosa fa esattamente il metodo post?


106

Ho riscontrato una caratteristica molto strana.

Quando provo a eseguire un'animazione sul thread principale, non si avvia. Quando corro detto animazione usando

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

Comincia.

Ho stampato il CurrentThreadprima di iniziare l'animazione ed entrambi vengono stampati main.

Ovviamente, mi manca qualcosa qui, poiché entrambi dovrebbero avviare l'animazione sul thread principale ... La mia ipotesi è che quando il post aggiunge l'attività alla coda, inizi in un momento più "corretto", ma mi piacerebbe saperlo cosa succede qui più in profondità.

EDIT: Permettimi di chiarire le cose: la mia domanda è, perché l'avvio dell'animazione sul post ne fa iniziare, quando l'avvio dell'animazione sul thread principale non lo fa.


Questo comportamento è specifico per una versione Android? Non sono riuscito a riprodurlo su Android 4.1.2!
Akdeniz

Ho riprodotto questo comportamento su Android 2.3.3. Ma per AnimationDrawable! L' Animationistanza ordinaria ha iniziato ad animarsi con successo su ogni configurazione. In AnimationDrawablecaso; quando si tenta di avviarlo onCreate, non si avvia perché non è stato collegato alla visualizzazione in quel momento. Quindi non è un problema di threading per AnimationDrawable. Forse vale la stessa cosa Animation? developer.android.com/guide/topics/graphics/…
Akdeniz

Risposte:


161

post : post fa sì che il Runnable venga aggiunto alla coda dei messaggi,

Runnable: rappresenta un comando che può essere eseguito. Spesso utilizzato per eseguire codice in un thread diverso.

run () : avvia l'esecuzione della parte attiva del codice della classe. Questo metodo viene chiamato quando viene avviato un thread che è stato creato con una classe che implementa Runnable.

getView().post(new Runnable() {

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

codice :getView().startAnimation(a);

nel tuo codice,

post fa sì che il Runnable (il codice verrà eseguito in un thread diverso) per aggiungere la coda dei messaggi.

Quindi startAnimation verrà attivato in un nuovo thread quando viene recuperato da messageQueue

[MODIFICA 1]

Perché usiamo un nuovo thread invece del thread dell'interfaccia utente (thread principale)?

Thread dell'interfaccia utente:

  • Quando l'applicazione viene avviata, il thread Ui viene creato automaticamente

  • è incaricato di inviare gli eventi ai widget appropriati e questo include gli eventi di disegno.

  • È anche il thread con cui interagisci con i widget Android

Ad esempio, se tocchi il pulsante a sullo schermo, il thread dell'interfaccia utente invia l'evento tocco al widget che a sua volta imposta il suo stato premuto e invia una richiesta di annullamento alla coda degli eventi. Il thread dell'interfaccia utente rimuove la richiesta e notifica al widget di ridisegnarsi.

Cosa succede se un utente preme un pulsante che farà longOperation?

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

L'interfaccia utente si blocca. Il programma potrebbe persino bloccarsi.

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

Infrange la regola di Android che non aggiorna mai l'interfaccia utente direttamente dal thread di lavoro

Android offre diversi modi per accedere al thread dell'interfaccia utente da altri thread.

  • Activity.runOnUiThread (Runnable)
  • View.post (Runnable)
  • View.postDelayed (eseguibile, lungo)
  • handler

Come sotto,

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

inserisci qui la descrizione dell'immagine

Per maggiori informazioni

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

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


15
Allora perché avviare l'animazione sul post è diverso rispetto a eseguirla sul thread principale, quando entrambi alla fine vengono eseguiti sullo stesso thread?
Gal

Perché questo modello a thread singolo può produrre scarse prestazioni nelle applicazioni Android.
Talha

1
Che cosa ha a che fare uno scarso rendimento con la mancata visualizzazione di un'animazione?
Gal

17
Non penso che questo risponda alla domanda, è più simile a una risposta generica per i principianti che non sanno nulla dell'interfaccia utente e del multi threading. Questo non spiega perché lanciare l'animazione in avanti nella coda fa funzionare l'animazione; un'animazione dovrebbe essere qualcosa da eseguire direttamente nell'interfaccia utente senza utilizzare alcun trucco post () o runOnUiThread ().
carrizo

3
Tutto il lavoro dell'interfaccia utente dovrebbe essere sul thread principale (thread dell'interfaccia utente). Il trucco che fa funzionare l'animazione usando post () invece di chiamare subito nel thread principaleèTempo: se chiami subito nel thread principale significa che hai detto "avvia l'animazione ora", ma in questo momento la vista potrebbe non essere pronta per l'animazione (misura, disegna ...). Ma se lo metti in post (), a volte sarà in attesa di startAnimation in coda per preparare la vista pronta per l'anim.
NguyenDat

35

Viene fatto su onCreate o onCreateView? In tal caso, l'app potrebbe non trovarsi in uno stato in cui la vista è collegata alla finestra. Molti algoritmi basati sulle metriche della vista potrebbero non funzionare poiché cose come le misurazioni e la posizione della vista potrebbero non essere state calcolate. Le animazioni Android in genere richiedono che vengano eseguite tramite la matematica dell'interfaccia utente

View.post mette effettivamente in coda l'animazione sul loop dei messaggi della vista, quindi una volta che la vista viene allegata alla finestra, esegue l'animazione invece di farla eseguire manualmente.

In realtà stai eseguendo le cose sul thread dell'interfaccia utente, ma in un momento diverso


La risposta accettata è fuorviante quando il poster afferma "il post causa il Runnable (il codice verrà eseguito in un thread diverso)". Questa è la risposta corretta "Stai effettivamente eseguendo le cose sul thread dell'interfaccia utente, ma in un momento diverso" - più 1
smitty1

18

Dai un'occhiata qui per una buona risposta. view.post () è più o meno lo stesso di handler.post (). Entra nella coda del thread principale e viene eseguito al termine delle altre attività in sospeso. Se chiami activity.runOnUiThread (), verrà chiamato immediatamente sul thread dell'interfaccia utente.


31
Una differenza enorme (ed estremamente utile) che ho trovato è che il runnable in view.post () verrà chiamato quando la vista viene mostrata per la prima volta. Ad esempio, è possibile impostarlo per avviare un'animazione all'inflazione della vista, quindi in futuro, aggiungerla infine alla gerarchia della vista. A quel punto, l'animazione verrà eseguita e non dovrai preoccupartene.
DeeV

In realtà, handler.post () non sempre pubblica un messaggio / eseguibile sul thread principale. Dipende da come viene creato il gestore (può essere associato a un Looper su un thread diverso). D'altra parte, view.post () verrà sempre eseguito sul thread principale
Yair Kukielka

4

Il problema penso che potrebbe essere il metodo del ciclo di vita in cui stai chiamando il metodo post (). Lo stai facendo in onCreate ()? in tal caso, guarda cosa ho trovato nella documentazione di onResume () dell'attività:

onResume ()

Aggiunto nel livello API 1 void onResume () Chiamato dopo onRestoreInstanceState (Bundle), onRestart () o onPause (), affinché la tua attività inizi a interagire con l'utente. Questo è un buon punto per iniziare le animazioni , aprire dispositivi ad accesso esclusivo (come la fotocamera), ecc.

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

Quindi, come ha detto Joe Plante, forse la vista non è pronta per avviare le animazioni nel momento in cui chiami post (), quindi prova a spostarla su onResume ().

PD: In realtà se sposti il ​​codice su onResume (), penso che tu possa rimuovere la chiamata post () poiché sei già nell'interfaccia utente e la vista dovrebbe essere pronta per avviare le animazioni.


3
onResumepuò essere richiamato più volte (gli schermi vanno a dormire, l'attività spinta in backstack, ecc ...) dopo che è inizialmente quando "la vista è pronta". Se chiamato da onResume, potrebbe essere necessaria una bandiera per tenere traccia del tempo in cui l'animazione è già stata avviata, per evitare (ri) ricominciare più volte.
samis
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.