2012-03-19 12 views
6

He estado jugando alrededor con Runnable s y han descubierto que si postDelayed un Runnable en un View luego retirar la devolución de llamada no va a funcionar, sin embargo si lo hace lo mismo pero publicar la Runnable en un Handler luego retirar la devolución de llamada hace trabajo.¿Por qué publicar y cancelar un ejecutable en una vista y el controlador dan como resultado bahviour diferente?

¿Por qué este trabajo (Runnable run() de código nunca se ejecutó):

Runnable runnable = new Runnable() { 
    @Override 
    public void run() { 
     // execute some code 
    } 
}; 

Handler handler = new Handler(); 
handler.postDelayed(runnable, 10000); 
handler.removeCallbacks(runnable); 

en los que no ya que esto hace (Runnable run() de código siempre es ejecutado) ?:

Runnable runnable = new Runnable() { 
    @Override 
    public void run() { 
     // execute some code 
    } 
}; 

View view = findViewById(R.id.some_view); 
view.postDelayed(runnable, 10000); 
view.removeCallbacks(runnable); 
+1

¿Ha sido comprobar el valor de retorno de 'removeCallbacks()'? – CommonsWare

+0

No había visto esto, ¿puede explicarme cómo puede ayudar esto? He leído la documentación, pero no veo cómo esto puede ayudar en mi ejemplo anterior. – Martyn

+2

'View.removeCallbacks()' siempre 'devuelve true;' (al menos en ICS - también es probable que descanse) [ver aquí] (http://grepcode.com/file/repository.grepcode.com/java/ext/ com.google.android/android/4.0.3_r1/android/view/View.java#8786) – zapl

Respuesta

4

Si el View no está conectado a una ventana, puedo ver que esto sucede, por cortesía de lo que parece un error en Android. Tácticamente, por lo tanto, puede ser una cuestión de tiempo, asegurándose de no publicar o eliminar el Runnable hasta después de que el View se adjunta a la ventana.

Si tiene un proyecto de ejemplo que reproduce este problema, me gustaría echarle un vistazo. De lo contrario, intentaré hacer el mío, así puedo tener algo que pueda usar para reportar mi presunto error.


ACTUALIZACIÓN

Como se ha mencionado en los comentarios, removeCallbacks() en widgets de trabajos más comunes, por lo que parece que este es un problema específico de WebView, por código de ejemplo de la OP.

+0

https://github.com/martynhaigh/RunnableCancelTest – Martyn

+0

@Martyn: Sí, como sospechaba, el 'WebView' no está conectado a la ventana en ese punto (por ejemplo, 'getWindowToken()' devuelve 'null'), por lo que se tropezará con este error en Android. Voy a presentar un problema sobre esto pronto. Mientras tanto, necesitarás usar 'Handler' para 'eliminarCallbacks()' de forma confiable. – CommonsWare

+0

@Martyn: Parece que el problema es más sutil de lo que pensaba. Intenté reproducir tu problema usando un 'Botón', y' removeCallbacks() 'tiene éxito, aunque el' Botón' no está conectado a la ventana en el momento del 'postDelayed()'. Ahora estoy adivinando que su dificultad podría ser más peculiar de 'WebView'. De todos modos, probablemente solo deberías usar un 'Handler' por ahora. – CommonsWare

0

Por diversas razones, el controlador de la vista (view.getHandler()) puede no estar listo cuando desea iniciar la animación.

Por lo tanto, probablemente debería esperar antes de asignar el ejecutable a la vista.

Suponiendo que usted está tratando de hacerlo desde dentro de una actividad, aquí es un código que espera a que el controlador esté disponible antes de publicar el ejecutable:

private void assignRunnable(final View view, final Runnable runnable, final int delay) 
{ 
    if (view.getHandler() == null) { 
    // View is not ready, postpone assignment 
    this.getView().postDelayed(new Runnable() { 
     @Override 
     public void run() { 
     assignRunnable(view, runnable, delay); 
     } 
    }, 100); 

    //Abort 
    return; 
    } 

    //View is ready, assign the runnable 
    view.postDelayed(runnable, delay); 
} 
0

En cuanto a ViewRootImpl.java, la semántica de View.removeCallbacks() parece poco claro por decir lo menos.

RunQueue.removeCallbacks simplemente elimina los Ejecutables de una ArrayList. Ver here.

Si se llama a RunQueue.executeActions antes de quitarCallbacks, entonces ArrayList se borra en todos los casos, haciendo que removeCallbacks no sea operativo. Ver here.

RunQueue.executeActions se llama para cada recorrido .... Consulte here.

Así que, a menos que me pierda algo, View.removeCallbacks no funcionará si ha ocurrido un cruce desde que llamó al View.post.

me quedo con @ comentario a James-Wald arriba y no uso View.post

Cuestiones relacionadas