¿Qué hace exactamente el método de publicación?

106

Me encontré con una característica muy extraña.

Cuando intento ejecutar una animación en el hilo principal, no se inicia. Cuando ejecuto dicha animación usando

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

Empieza.

Imprimí el CurrentThreadantes de comenzar la animación y ambos imprimieron main.

Obviamente, me falta algo aquí, ya que ambos deberían iniciar la animación en el hilo principal ... Supongo que cuando la publicación agrega la tarea a la cola, comienza en un "momento correcto", pero me encantaría saber lo que sucede aquí con más profundidad.

EDITAR: Permítanme aclarar las cosas: mi pregunta es, por qué iniciar la animación en la publicación hace que se inicie, cuando no se inicia la animación en el hilo principal.

Galón
fuente
¿Este comportamiento es específico de una versión de Android? ¡No pude reproducirlo en Android 4.1.2!
Akdeniz
Reproduje este comportamiento en Android 2.3.3. ¡Pero para AnimationDrawable! La Animationinstancia ordinaria comenzó a animarse con éxito en cada configuración. En AnimationDrawablecaso; cuando intenta iniciarlo onCreate, no se inicia porque no está adjunto a la vista en ese momento. Por lo tanto, no es un problema de subprocesos AnimationDrawable. ¿Quizás se aplica lo mismo Animation? developer.android.com/guide/topics/graphics/…
Akdeniz

Respuestas:

161

post : post hace que el Runnable se agregue a la cola de mensajes,

Ejecutable: representa un comando que se puede ejecutar. A menudo se usa para ejecutar código en un hilo diferente.

run () : comienza a ejecutar la parte activa del código de la clase. Este método se llama cuando se inicia un hilo que se ha creado con una clase que implementa Runnable.

getView().post(new Runnable() {

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

codigo :getView().startAnimation(a);

en tu código,

post hace que Runnable (el código se ejecutará en un hilo diferente) para agregar la cola de mensajes.

Por lo tanto, startAnimation se activará en un nuevo hilo cuando se obtenga del messageQueue

[EDITAR 1]

¿Por qué usamos un nuevo hilo en lugar del hilo de la interfaz de usuario (hilo principal)?

Hilo de la interfaz de usuario:

  • Cuando se inicia la aplicación, Ui Thread se crea automáticamente

  • está a cargo de enviar los eventos a los widgets apropiados y esto incluye los eventos de dibujo.

  • También es el hilo con el que interactúas con los widgets de Android.

Por ejemplo, si toca el botón a en la pantalla, el hilo de la interfaz de usuario envía el evento táctil al widget, que a su vez establece su estado presionado y publica una solicitud de invalidación en la cola de eventos. El hilo de la interfaz de usuario quita la cola de la solicitud y notifica al widget que se redibuje.

¿Qué sucede si un usuario presiona un botón que hará una operación prolongada?

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

La interfaz de usuario se congela. El programa incluso puede fallar.

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

Rompe la regla de Android que nunca actualiza la interfaz de usuario directamente desde el hilo del trabajador

Android ofrece varias formas de acceder al hilo de la interfaz de usuario desde otros hilos.

  • Activity.runOnUiThread (ejecutable)
  • View.post (ejecutable)
  • View.postDelayed (ejecutable, largo)
  • Manipulador

Como abajo,

View.post (ejecutable)

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

Manipulador

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

ingrese la descripción de la imagen aquí

Para más información

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
fuente
15
Entonces, ¿por qué comenzar la animación en la publicación es diferente a ejecutarla en el hilo principal, cuando ambos finalmente se ejecutan en el mismo hilo?
Gal
Porque este modelo de un solo hilo puede producir un rendimiento deficiente en aplicaciones de Android.
Talha
1
¿Qué tiene que ver el bajo rendimiento con no mostrar una animación?
Gal
17
No creo que esto responda a la pregunta, es más como una respuesta genérica para principiantes que no saben nada sobre el ui-thread y el multi-hilo. Esto no explica por qué lanzar la animación en la cola hace que la animación funcione; Se supone que una animación es algo para ejecutar directamente en el ui-thread sin usar ningún truco post () o runOnUiThread ().
Carrizo
3
Todo el trabajo de la interfaz de usuario debe estar en el hilo principal (hilo de la interfaz de usuario). El truco que hace que la animación funcione usando post () en lugar de llamar en el hilo principal de inmediato es Tiempo: si llamas de inmediato en el hilo principal, eso significa que dijiste "iniciar animación ahora", pero en este momento es posible que la vista no esté lista para animación (medir, dibujar ...). Pero si pones eso en post (), estará pendiente de startAnimation en la cola en algún momento para preparar la vista lista para anim.
NguyenDat
35

¿Se está haciendo esto en onCreate o onCreateView? Si es así, es posible que la aplicación no esté en un estado en el que la Vista esté adjunta a la ventana. Es posible que muchos algoritmos basados ​​en métricas de vista no funcionen, ya que es posible que no se hayan calculado cosas como las medidas y la posición de la vista. Las animaciones de Android generalmente requieren que se ejecuten a través de matemáticas de interfaz de usuario

View.post en realidad pone en cola la animación en el bucle de mensajes de la Vista, por lo que una vez que la vista se adjunta a la ventana, ejecuta la animación en lugar de hacer que se ejecute manualmente.

En realidad, está ejecutando cosas en el hilo de la interfaz de usuario, pero en un momento diferente

Joe Plante
fuente
La respuesta aceptada es engañosa cuando el cartel dice "la publicación provoca el Runnable (el código se ejecutará en un hilo diferente)". Esta es la respuesta correcta "Realmente estás ejecutando cosas en el hilo de la IU, pero en un momento diferente", más 1
smitty1
18

Eche un vistazo aquí para obtener una buena respuesta. view.post () es lo mismo que handler.post () prácticamente. Entra en la cola de subprocesos principal y se ejecuta después de que finalizan las otras tareas pendientes. Si llama a activity.runOnUiThread (), se llamará inmediatamente en el hilo de la interfaz de usuario.

Tas Morf
fuente
31
Una diferencia masiva (y extremadamente útil) que he encontrado es que se llamará al ejecutable en view.post () cuando se muestre la Vista por primera vez. Es decir, puede configurarlo para que comience una animación al inflar la Vista y luego, en algún momento en el futuro, finalmente agregarlo a la jerarquía de la vista. En ese momento, la animación se ejecutará y no tendrá que preocuparse por ello.
DeeV
En realidad, handler.post () no siempre publica un mensaje / ejecutable en el hilo principal. Depende de cómo se cree el controlador (se puede asociar a un Looper en un hilo diferente). Por otro lado, view.post () siempre se ejecutará en el hilo principal
Yair Kukielka
4

Creo que el problema podría ser el método de ciclo de vida en el que está llamando al método post (). ¿Lo estás haciendo en onCreate ()? si es así, mire lo que encontré en la documentación de onResume () de la actividad:

En resumen()

Agregado en API nivel 1 void onResume () Llamado después de onRestoreInstanceState (Bundle), onRestart () o onPause (), para que su actividad comience a interactuar con el usuario. Este es un buen lugar para comenzar animaciones , abrir dispositivos de acceso exclusivo (como la cámara), etc.

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

Entonces, como dijo Joe Plante, tal vez la vista no esté lista para iniciar animaciones en el momento en que llama a post (), así que intente moverla a onResume ().

PD: En realidad, si mueve el código a onResume (), entonces creo que puede eliminar la llamada post () ya que ya está en el hilo de la interfaz de usuario y la vista debería estar lista para iniciar las animaciones.

carrizo
fuente
3
onResumese puede llamar varias veces (las pantallas se suspenden, la actividad se desplaza al backstack, etc.) después de que sea inicialmente cuando "la vista está lista". Si se llama desde onResume, es posible que se necesite una bandera para realizar un seguimiento del tiempo en que la animación ya se ha iniciado, para evitar (re) iniciar varias veces.
samis