O que exatamente o método de postagem faz?

106

Eu encontrei um recurso muito estranho.

Quando tento executar uma animação no thread principal, ela não inicia. Quando executo essa animação usando

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

Ele começa.

Imprimi o CurrentThreadantes de iniciar a animação e ambos imprimem main.

Obviamente, estou faltando alguma coisa aqui, pois ambos devem iniciar a animação no thread principal ... Meu palpite é que conforme a postagem adiciona a tarefa à fila, ela começa em um "horário mais correto", mas eu adoraria saber o que acontece aqui com mais profundidade.

EDIT: Deixe-me esclarecer as coisas - minha pergunta é, por que iniciar a animação no post faz com que ela comece, ao passo que iniciar a animação no thread principal não.

Garota
fonte
Este comportamento é específico de uma versão do Android? Não consegui reproduzir no Android 4.1.2!
Akdeniz
Reproduzi esse comportamento no Android 2.3.3. Mas para AnimationDrawable! A Animationinstância comum começou a ser animada com sucesso em cada configuração. No AnimationDrawablecaso; quando você tenta iniciá-lo onCreate, ele não inicia por não estar conectado à visualização naquele momento. Portanto, não é um problema de segmentação AnimationDrawable. Talvez a mesma coisa se aplique Animation? developer.android.com/guide/topics/graphics/…
Akdeniz

Respostas:

161

post : post faz com que o Runnable seja adicionado à fila de mensagens,

Executável: representa um comando que pode ser executado. Frequentemente usado para executar código em um Thread diferente.

run () : inicia a execução da parte ativa do código da classe. Este método é chamado quando um thread é iniciado que foi criado com uma classe que implementa Runnable.

getView().post(new Runnable() {

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

código :getView().startAnimation(a);

em seu código,

post faz com que o Runnable (o código será executado em um thread diferente) adicione a fila de mensagens.

Portanto, startAnimation será acionado em uma nova thread quando for buscado em messageQueue

[EDITAR 1]

Por que usamos um novo thread em vez de UI thread (thread principal)?

Tópico da interface do usuário:

  • Quando o aplicativo é iniciado, Ui Thread é criado automaticamente

  • é responsável por despachar os eventos para os widgets apropriados e isso inclui os eventos de desenho.

  • É também o tópico com o qual você interage com widgets Android

Por exemplo, se você tocar no botão a na tela, o encadeamento da IU despacha o evento de toque para o widget que, por sua vez, define seu estado pressionado e posta uma solicitação de invalidação na fila de eventos. O encadeamento da IU retira a solicitação e notifica o widget para se redesenhar.

O que acontece se um usuário pressionar um botão que fará longOperation?

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

A IU congela. O programa pode até travar.

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

Ele quebra a regra do Android de nunca atualizar a IU diretamente do thread de trabalho

O Android oferece várias maneiras de acessar o thread de IU de outros threads.

  • Activity.runOnUiThread (Executável)
  • View.post (executável)
  • View.postDelayed (executável, longo)
  • Handler

Como abaixo,

View.post (executável)

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

insira a descrição da imagem aqui

Para mais informações

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

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

Talha
fonte
15
Então, por que iniciar a animação na postagem é diferente de executá-la no thread principal, quando ambos, eventualmente, são executados no mesmo thread?
Gal
Porque este modelo de thread único pode resultar em baixo desempenho em aplicativos Android.
Talha
1
O que o desempenho ruim tem a ver com não mostrar uma animação?
Gal
17
Não acho que isso responda à pergunta, é mais como uma resposta genérica para iniciantes que não sabem nada sobre ui-thread e multi-threading. Isso não explica por que lançar a animação à frente na fila faz com que ela funcione; uma animação deve ser algo a ser executado diretamente no ui-thread sem usar nenhum truque post () ou runOnUiThread ().
carrizo
3
Todo o trabalho da IU deve estar no thread principal (thread da IU). O truque que faz a animação funcionar usando post () em vez de chamar no thread principal imediatamente é o tempo: se você chamar imediatamente no thread principal, isso significa que você disse "comece a animação agora", mas neste momento pode ser que a visualização não esteja pronta para animação (medir, desenhar ...). Mas se você colocar isso em post (), ele aguardará startAnimation na fila para preparar a visualização pronta para anim.
NguyenDat
35

Isso está sendo feito em onCreate ou onCreateView? Nesse caso, o aplicativo pode não estar em um estado em que a Visualização esteja anexada à janela. Muitos algoritmos baseados nas métricas do View podem não funcionar, pois coisas como as medidas e a posição do View podem não ter sido calculadas. As animações do Android normalmente exigem que executem matemática da IU

View.post, na verdade, enfileira a animação no loop de mensagem da View, portanto, uma vez que a view é anexada à janela, ela executa a animação em vez de fazê-la manualmente.

Na verdade, você está executando coisas no thread da IU, mas em um momento diferente

Joe Plante
fonte
A resposta aceita é enganosa onde o postador afirma "a postagem causa o Runnable (o código será executado em um tópico diferente)". Esta é a resposta correta "Você está realmente executando coisas no thread de IU, mas em um momento diferente" - mais 1
smitty1
18

Dê uma olhada aqui para uma boa resposta. view.post () é basicamente o mesmo que handler.post (). Ele vai para a fila de threads principal e é executado após a conclusão das outras tarefas pendentes. Se você chamar activity.runOnUiThread (), ele será chamado imediatamente no thread de interface do usuário.

Tas Morf
fonte
31
Uma diferença enorme (e extremamente útil) que encontrei é o executável em view.post () será chamado quando o View for mostrado pela primeira vez. Ou seja, você pode configurá-lo para iniciar uma animação ao aumentar a visualização e, em algum ponto no futuro, finalmente adicioná-lo à hierarquia de visualização. Nesse ponto, a animação será executada e você não terá que se preocupar com isso.
DeeV
Na verdade, handler.post () nem sempre posta uma mensagem / executável no thread principal. Depende de como o manipulador é criado (pode ser associado a um Looper em uma thread diferente). Por outro lado, view.post () sempre será executado no thread principal
Yair Kukielka
4

O problema, eu acho, pode ser o método de ciclo de vida em que você está chamando o método post (). Você está fazendo isso em onCreate ()? em caso afirmativo, veja o que encontrei na documentação onResume () da atividade:

Resumindo()

Adicionado na API de nível 1 void onResume () Chamado após onRestoreInstanceState (Bundle), onRestart () ou onPause (), para que sua atividade comece a interagir com o usuário. Este é um bom lugar para começar animações , abrir dispositivos de acesso exclusivo (como a câmera), etc.

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

Portanto, como disse Joe Plante, talvez a visualização não esteja pronta para iniciar animações no momento em que você chama post (), então tente movê-la para onResume ().

PD: Na verdade, se você mover o código para onResume (), então acho que você pode remover a chamada post (), uma vez que você já está no ui-thread e a visualização deve estar pronta para iniciar as animações.

carrizo
fonte
3
onResumepode ser chamado várias vezes (as telas hibernam, atividade empurrada para o backstack, etc ...) após ser inicialmente quando "a visualização está pronta". Se chamado de onResume, um sinalizador pode ser necessário para rastrear o tempo em que a animação já foi iniciada, para evitar (re) iniciar várias vezes.
samis