Ich versuche, den besten Anwendungsfall der Verwendung zu verstehen HandlerThread
.
Laut Definition:
“Praktische Klasse zum Starten eines neuen Threads, der einen Looper hat. Der Looper kann dann verwendet werden, um Handler-Klassen zu erstellen. Beachten Sie, dass start() immer noch aufgerufen werden muss.”
Ich kann mich irren, aber eine ähnliche Funktionalität kann ich mit a erreichen Thread
, Looper
und Handler
. Wann sollte ich also verwenden HandlerThread
? Ein Beispiel wäre wirklich hilfreich.
Hier ist ein Beispiel aus dem wirklichen Leben, wo HandlerThread wird handlich. Wenn Sie sich für Kamera-Vorschaurahmen registrieren, erhalten Sie diese onPreviewFrame()
Rückrufen. Die Dokumentation erklärt das Dieser Rückruf wird bei dem Ereignis-Thread aufgerufen, von dem aus open(int) aufgerufen wurde.
Normalerweise bedeutet dies, dass der Rückruf im Hauptthread (UI) aufgerufen wird. Daher kann die Aufgabe, mit den riesigen Pixelarrays umzugehen, hängen bleiben, wenn Menüs geöffnet, Animationen animiert oder sogar Statistiken auf dem Bildschirm gedruckt werden.
Die einfache Lösung besteht darin, eine new HandlerThread()
und delegieren Camera.open()
zu diesem Thread (ich habe es durch post(Runnable)
müssen Sie nicht implementieren Handler.Callback
).
Beachten Sie, dass alle anderen Arbeiten mit der Kamera wie gewohnt erledigt werden können, Sie müssen sie nicht delegieren Camera.startPreview()
oder Camera.setPreviewCallback()
zum HandlerThread. Um auf der sicheren Seite zu sein, ich Warten für das Eigentliche Camera.open(int)
zu vervollständigen, bevor ich mit dem Haupt-Thread fortfahre (oder welchen Thread auch immer zum Aufrufen verwendet wurde Camera.open()
vor der Änderung).
Also, wenn Sie mit Code beginnen
try {
mCamera = Camera.open(1);
}
catch (RuntimeException e) {
Log.e(LOG_TAG, "failed to open front camera");
}
// some code that uses mCamera immediately
erst extrahieren wie es ist in eine private Methode:
private void oldOpenCamera() {
try {
mCamera = Camera.open(1);
}
catch (RuntimeException e) {
Log.e(LOG_TAG, "failed to open front camera");
}
}
und statt anzurufen oldOpenCamera()
einfach verwenden newOpencamera()
:
private void newOpenCamera() {
if (mThread == null) {
mThread = new CameraHandlerThread();
}
synchronized (mThread) {
mThread.openCamera();
}
}
private CameraHandlerThread mThread = null;
private static class CameraHandlerThread extends HandlerThread {
Handler mHandler = null;
CameraHandlerThread() {
super("CameraHandlerThread");
start();
mHandler = new Handler(getLooper());
}
synchronized void notifyCameraOpened() {
notify();
}
void openCamera() {
mHandler.post(new Runnable() {
@Override
public void run() {
oldOpenCamera();
notifyCameraOpened();
}
});
try {
wait();
}
catch (InterruptedException e) {
Log.w(LOG_TAG, "wait was interrupted");
}
}
}
Beachten Sie, dass das Ganze benachrichtigen() — Warten() Inter-Thread-Kommunikation ist nicht erforderlich, wenn Sie nicht darauf zugreifen mKamera im Originalcode sofort nach dem Öffnen.
Aktualisieren: Hier wird der gleiche Ansatz auf den Beschleunigungssensor angewendet: Acclerometer Sensor in Separate Thread
Hier ist ein Link zum Quellcode für HandlerThread und Schleifer.
Wenn Sie sich die beiden ansehen, werden Sie sehen, dass a HandlerThread
ist genau das, was es verspricht – ein bequemer Weg, um damit zu beginnen Thread
das hat ein Looper
. Warum gibt es das? Weil Threads haben standardmäßig keine Nachrichtenschleife. Die HandlerThread
ist nur eine einfache Möglichkeit, eine zu erstellen, die dies tut. Könntest du diese Funktion mit duplizieren Handler
, Thread
und Looper
– nach dem Quellcode zu urteilen – lautet die Antwort ja.
Ein Executor
ist anders. Ein Executor
nimmt eingereichte ausführbare Aufgaben und – ratet mal – führt sie aus. Warum ist das notwendig? Es erlaubt Ihnen die Ausführung der Aufgabe von ihrem eigentlichen Inhalt zu entkoppeln. Wann würden Sie dies verwenden? Angenommen, Sie hatten eine Situation, in der mehrere Aufgaben gleichzeitig ausgeführt werden mussten. Sie können wählen, indem Sie ein verwenden Executor
, um sie alle in einem einzigen Thread auszuführen, sodass sie seriell ausgeführt werden. Oder Sie könnten einen festen Thread-Pool verwenden, sodass einige, aber nicht alle gleichzeitig ausgeführt werden. In beiden Fällen ist der Inhalt der Aufgabe – dh was sie tatsächlich tut – von der Art und Weise, wie sie ausgeführt wird, getrennt.
Ich habe HandlerThread nie verwendet, aber vielleicht hilft dieser Beitrag – stackoverflow.com/questions/7462098/…
– Tal Kanel
9. August 2013 um 15:14 Uhr
@TalKanel: Danke für die Antwort. Ich habe diesen Beitrag überprüft, aber er spricht über den Nutzen von Executor gegenüber HandlerThread, dem ich voll und ganz zustimme. Meine Frage ist eher, was der beste Anwendungsfall für die Verwendung von HandlerThread ist
– Androidme
9. August 2013 um 15:20 Uhr
Dieser Beitrag geht auch auf diese Frage ein. Executoren sind flexibler, da sie einen Pool von Threads verwalten können. Mehrere Threads – Sie müssen einen Executor verwenden. Einzelner Thread – Sie können einen HandlerThread verwenden. Ich bin mir nicht sicher, ob es wichtig ist, da der Testamentsvollstrecker beide Situationen abdecken könnte. Wie ich es verstanden habe, können Sie mit einem HandlerThread nur einen Handler in einem Hintergrundthread erstellen – etwas, das ohnehin auf andere Weise möglich ist.
– Rar
9. August 2013 um 15:28 Uhr
Siehe meinen Kommentar hier – stackoverflow.com/questions/17897536/how-to-handle-tcp-data/…. Im Allgemeinen bevorzuge ich
HandlerThread
s durch die supereinfache Synchronisierung viaMessage
S. Allerdings kann es auch ein Engpass sein. Außerdem hindert Sie nichts daran, einen ExecutorService mit HandlerThreads zu verwenden. Ich habe schon mal eins geschrieben, es ist nicht so schwer.– Deljan
9. August 2013 um 15:30 Uhr
@Rarw: Das war eigentlich meine Frage, ob wir Dinge auf andere Weise tun können, was nötig ist, um eine andere Klasse zu erstellen. Ich bin mir sicher, dass es in speziellen Szenarien einige Vorteile gibt, die ich kennen muss.
– Androidme
9. August 2013 um 15:50 Uhr