2015-03-06 8 views
12

Si consideri il seguente frammento di codice:Cosa succede se un gestore invia un messaggio a un thread dopo Looper.prepare() ma prima che Looper.loop() sia stato chiamato?

Looper.prepare(); 
handler = new Handler() { 
     @Override 
     public void handleMessage(Message msg) { 
      super.handleMessage(msg); 
      getLooper().quitSafely(); 
     } 
    }; 
for(int i = 0; i < urls.size(); i++) { 
    useCaseProvider.get().execute(callback, handler, urls.get(i), threadPool); 
} 
Looper.loop(); 

//Continue processing the results of all the use cases after the  
//loop has been asked to terminated via the handler 

Un po 'di storia: sto facendo un po' di elaborazione sul thread dell'interfaccia utente in cui ho bisogno di eseguire il ping un grande su di dispositivi e fare qualcosa con il risultato. Ho bisogno di eseguire le richieste in parallelo per essere efficiente.

Domanda: se uno di questi casi d'uso è stato eseguito in qualche modo abbastanza veloce e ha fatto una richiamata prima che potessi colpire Looper.loop(); il messaggio verrebbe accodato o semplicemente perso? I callback vengono postati su questo thread dal gestore che esegue una eseguibile sul thread originale.

risposta

7

Supponendo che tu abbia richiamato Looper.prepare() prima che il tuo useCaseProvider fornisca i risultati, dovresti stare bene. Se Looper.prepare non è stato chiamato dovresti vedere generare RuntimeException.

L'oggetto Looper è collegato a un thread locale che ospita la coda messaggi. La funzione Looper.prepare costruirà questa coda di messaggi a quel punto è possibile iniziare a mettere in coda i messaggi. Una volta attivato Looper.loop(), è in quel momento che i messaggi in sospeso inizieranno a essere eseguiti.

Guardando il frammento, non sono troppo sicuro di come le cose sono legate insieme. Generalmente si vuole costruire un looper come questo:

private static final class MyThread extends Thread { 
    private Handler mHandler; 

    @Override 
    public void run() { 
     Looper.prepare(); 

     mHandler = new Handler() { 
      @Override 
      public void handleMessage(Message msg) { 
       // handle message 
      } 
     }; 

     Looper.loop(); 
    } 

    public Handler getHandler() { 
     return mHandler; 
    } 
} 

sto supponendo che il pool di thread è quindi un pool di thread MyThread, ognuno dei quali hanno un proprio Looper. Il pool di thread dovrebbe inizializzare i thread in modo che, una volta consegnato un Runnable che deve essere eseguito dal thread, il metodo run() debba avere il Looper inizializzato.

D'altra parte, se si desidera associare il gestore con un looper particolare (ad esempio non si sta costruendo il gestore all'interno di un thread come sopra), è necessario passare il thread Looper al costruttore come:

Handler h = new Handler(myLooperThread); 

Se non lo si specifica, il gestore utilizza il thread in cui è stato creato per prelevare il Looper di quel thread dall'oggetto ThreadLocal.

Infine, se le tue intenzioni sono di consegnare messaggi sul Gestore associato al thread dell'interfaccia utente, non dovresti preoccuparti di chiamare Looper.prepare o Looper.loop. Questo è gestito dall'attività.

+0

Quindi, fondamentalmente nel suo snippet i messaggi di elaborazione sul 'MessageQueue' saranno ritardati dal tempo di esecuzione del ciclo' for'. – Emmanuel

+0

Praticamente. Qualsiasi tentativo di consegna a un Looper non inizializzato comporterà un'eccezione di runtime. A seconda di cosa è necessario, un IntentService potrebbe essere un modo migliore per andare. – jagsaund