English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Spiegazione dettagliata delle relazioni tra Thread, Handler e HandlerThread

Premessa

Pochi giorni fa ho visto una domanda di intervista: Qual è la differenza tra Thread, Handler e HandlerThread? Questa domanda è interessante, per molte persone potrebbe essere familiare con Thread e Handler, che sono principalmente coinvolti nel meccanismo di messaggi di Android (Handler, Message, Looper, MessageQueue), vedi anche "Riassunto del meccanismo di messaggi di Android da Handler.post(Runnable r) - Esperienze e memorie"

Ma a cosa serve questo HandlerThread? È un Handler o un Thread? Sappiamo che l'Handler viene utilizzato per aggiornare l'UI in modo asincrono, più dettagliatamente, per comunicare tra thread, quando aggiorniamo l'UI è la comunicazione tra il thread secondario e il thread principale dell'UI. Quindi, se vogliamo comunicare tra thread secondari, come fare? Alla fine, anche questo può essere fatto utilizzando Handler+Thread (non raccomandato, richiede operazioni manuali su Looper), Google ha gentilmente encapsulato una classe per noi, ovvero il già menzionato: HandlerThread. (Per scenari multi-thread, ci sono anche encapsulamenti simili come AsyncTask)

Metodo di utilizzo

Vediamo prima come utilizzare HandlerThread:
Prima crea un HandlerThread e esegui start()

private HandlerThread mHandlerThread;
......
mHandlerThread = new HandlerThread("HandlerThread");
handlerThread.start();

Crea un Handler, utilizzando mHandlerThread.getLooper() per generare Looper:

 final Handler handler = new Handler(mHandlerThread.getLooper()){
   @Override
   public void handleMessage(Message msg) {
    System.out.println("ricevuto messaggio");
   }
  });

Poi crea un thread secondario per inviare messaggi:

 new Thread(new Runnable() {
   @Override
   public void run() {
    try {
     Thread.sleep(1000);//operazione di consumo di tempo simulata
     handler.sendEmptyMessage(0);
    }
     e.printStackTrace();
    }
   }
  }).start();

Infine, non dimenticare di rilasciare in onDestroy per evitare le perdite di memoria:

 @Override
 protected void onDestroy() {
  super.onDestroy();
  mHandlerThread.quit();
 }

Il risultato dell'esecuzione è molto semplice, ovvero stampare la stringa nella console: ricevuto messaggio

Principio

Nel processo di utilizzo intero non dobbiamo preoccuparci delle cose correlate a Handler, è sufficiente inviare messaggi, elaborare messaggi, e le cose correlate a Looper vengono lasciate al loro stesso processo di elaborazione. Vediamo ora il codice sorgente per capire come viene implementato, iniziamo con il costruttore:

public class HandlerThread extends Thread {}

HandlerThread è ancora una thread, cosa lo distingue da una thread comune?

public class HandlerThread extends Thread {
 int mPriority;
 int mTid = -1;
 Looper mLooper;
 public HandlerThread(String name) {
  super(name);
  mPriority = Process.THREAD_PRIORITY_DEFAULT;
 }
 ......
}

La risposta è che c'è un altro Looper, questo è il Looper esclusivo della sottolinea di thread, usato per estrarre e gestire i messaggi. Continuiamo a guardare il metodo run() della classe thread HandlerThread: 

 protected void onLooperPrepared() {
 }
 @Override
 public void run() {
  mTid = Process.myTid();
  Looper.prepare();
  synchronized (this) {
   mLooper = Looper.myLooper(); // Genera Looper
   notifyAll();
  }
  Process.setThreadPriority(mPriority);
  onLooperPrepared(); // Metodo vuoto, chiamato dopo la creazione del Looper, può essere sovrascritto per logica personalizzata
  Looper.loop(); // Ciclo infinito, che continua a prendere messaggi dalla MessageQueue e a consegnarli al Handler
  mTid = -1;
 }

Principalmente si tratta di alcune operazioni su Looper, se lo facciamo noi stessi usando Handler+Thread, dobbiamo eseguire questa operazione. Vediamo anche il metodo getLooper():   

public Looper getLooper() {
  if (!isAlive()) {
   return null;
  }
  // Se il thread è stato avviato, attendi fino a che il looper è stato creato.
  synchronized (this) {
   while (isAlive() && mLooper == null) {
    try {
     wait();
    }
    }
   }
  }
  return mLooper;
 }

Il metodo è semplice, aggiungendo un blocco di sincronizzazione, se è stato creato (isAlive() restituisce true) ma mLooper è vuoto, continua a attendere fino a che mLooper viene creato con successo, infine controlla il metodo quit, è degno di nota che ci sono due di essi:

 public boolean quit() {
  Looper looper = getLooper();
  if (looper != null) {
   looper.quit();
   return true;
  }
  return false;
 }
 public boolean quitSafely() {
  Looper looper = getLooper();
  if (looper != null) {
   looper.quitSafely();
   return true;
  }
  return false;
 }

quitSafely è utilizzato per fermare i messaggi in coda o i messaggi in ritardo non elaborati. Dopo aver chiamato questo metodo, tutti questi messaggi saranno fermati.

Sommario

Il metodo di utilizzo di HandlerThread è piuttosto semplice, ma dobbiamo capire un punto fondamentale: se una thread deve elaborare messaggi, deve avere il proprio Looper, non è sufficiente creare un Handler per elaborare messaggi ovunque.

Se non si utilizza HandlerThread, è necessario chiamare manualmente i metodi come Looper.prepare() e Looper.loop().

Di seguito è riassunta la raccolta delle informazioni sulla relazione tra Thread, Handler e HandlerThread, ulteriori informazioni saranno aggiunte in seguito, grazie per il supporto della nostra community!

Dichiarazione: il contenuto di questo articolo è stato raccolto da Internet, il copyright è di proprietà del rispettivo autore, il contenuto è stato contribuito e caricato autonomamente dagli utenti di Internet, questo sito non detiene i diritti di proprietà, non è stato editato manualmente e non assume responsabilità legali correlate. Se trovi contenuti sospetti di violazione del copyright, sei invitato a inviare una e-mail a: notice#oldtoolbag.com (al momento dell'invio dell'e-mail, sostituisci # con @) per segnalare, fornendo prove pertinenti. Una volta verificata, questo sito eliminerà immediatamente il contenuto sospetto di violazione del copyright.

Ti potrebbe interessare