2012-03-19 6 views
6

Ho giocato in giro con Runnable s e hanno scoperto che se si postDelayed un Runnable su un View quindi rimuovendo il callback non funziona, se si fa lo stesso, ma pubblicare il Runnable su un Handler quindi rimuovendo il callback fa lavoro.Perché la pubblicazione e l'annullamento di una persona eseguibile su una vista e un gestore risultano in un altro bahviour?

Perché questo lavoro (codice Runnable run() non viene mai eseguito):

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

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

dove non come questo non fa (sempre viene eseguito il codice Runnable run()) ?:

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

Sei stato controllando il valore restituito da 'removeCallbacks()'? – CommonsWare

+0

Non l'avevo visto, puoi spiegare come può essere d'aiuto? Ho letto la documentazione ma non vedo come questo possa essere d'aiuto nel mio esempio precedente. – Martyn

+2

'View.removeCallbacks()' restituirà sempre true; '(almeno su ICS - probabilmente anche il resto) [vedere qui] (http://grepcode.com/file/repository.grepcode.com/java/ext/ com.google.android/android/4.0.3_r1/android/view/View.java#8786) – zapl

risposta

4

Se lo View non è collegato a una finestra, posso vedere che questo accade, per gentile concessione di quello che sembra un bug in Android. Tatticamente, quindi, potrebbe essere una questione di tempistica, assicurandosi di non pubblicare o rimuovere lo Runnable fino a quando lo View è collegato alla finestra.

Se vi capita di avere un progetto di esempio in giro che replica questo problema, vorrei dargli un'occhiata. Altrimenti, proverò a creare il mio, quindi posso avere qualcosa che posso usare per segnalare il mio presunto bug.


UPDATE

Come accennato nei commenti, removeCallbacks() sui widget più comuni opere, in modo che appaia questo è un problema SPECIFICI WebView, per codice di esempio del PO.

+0

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

+0

@Martyn: Sì, come sospettavo, il 'WebView' non è collegato alla finestra a quel punto (ad esempio, 'getWindowToken()' restituisce 'null'), e quindi si sta per inciampare su questo bug in Android. Presenterò un problema su questo presto. Nel frattempo, dovrai usare 'Handler' per affidabilmente' removeCallbacks() '. – CommonsWare

+0

@ Martin: Sembra che il problema sia più sottile di quanto pensassi. Ho provato a riprodurre il problema usando un 'Button', e' removeCallbacks() 'ha successo, anche se il' Button' non è collegato alla finestra al momento del 'postDelayed()'. Ora sto indovinando che la tua difficoltà potrebbe essere più peculiare di 'WebView'. Indipendentemente, probabilmente dovresti semplicemente usare un 'Handler' per ora. – CommonsWare

0

Per vari motivi, il gestore della vista (view.getHandler()) potrebbe non essere pronto quando si desidera avviare l'animazione.

Pertanto è necessario attendere prima di assegnare la funzione eseguibile alla vista.

Supponendo che si sta tentando di fare, che all'interno di un attività, ecco un codice che attende per il gestore sia disponibile prima di pubblicare l'eseguibile:

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

Guardando ViewRootImpl.java, la semantica di View.removeCallbacks() non è chiaro a dir poco.

RunQueue.removeCallbacks rimuove solo i Runnables da un ArrayList. Vedi here.

Se RunQueue.executeActions viene chiamato prima removeCallbacks, allora l'ArrayList viene cancellato in tutti i casi che fanno removeCallbacks un no-op. Vedi here.

RunQueue.executeActions è chiamato per ogni attraversamento .... Vedi here.

Quindi, a meno che non mi sfugga qualcosa, View.removeCallbacks non funzionerà se è avvenuta una traversata da quando hai chiamato View.post.

mi atterrò a @ commento james-Wald sopra e non uso View.post

Problemi correlati