Лучшее использование HandlerThread над другими подобными классами
Я пытаюсь понять лучший вариант использования HandlerThread
,
Согласно определению:
"Удобный класс для запуска нового потока, в котором есть петлитель. Затем петлитель можно использовать для создания классов-обработчиков. Обратите внимание, что start() по-прежнему должен вызываться".
Я могу ошибаться, но схожую функциональность я могу достичь, используя Thread
, Looper
а также Handler
, Так, когда я должен использовать HandlerThread
? Пример был бы действительно полезен.
2 ответа
Вот реальный пример из жизни, где HandlerThread становится удобным. Когда вы регистрируетесь для предварительного просмотра кадров камеры, вы получаете их в onPreviewFrame()
Перезвоните. В документации объясняется, что этот обратный вызов вызывается в потоке событий, из которого вызывается open (int).
Обычно это означает, что обратный вызов будет вызван в основном потоке (UI). Таким образом, задача работы с огромными пиксельными массивами может застрять при открытии меню, анимации или даже при выводе статистики на экран.
Простое решение - создать new HandlerThread()
и делегировать Camera.open()
к этой теме (я сделал это через post(Runnable)
вам не нужно реализовывать Handler.Callback
).
Обратите внимание, что вся другая работа с камерой может быть выполнена как обычно, вам не нужно делегировать Camera.startPreview()
или же Camera.setPreviewCallback()
в HandlerThread. Чтобы быть в безопасности, я жду фактического Camera.open(int)
завершить, прежде чем я продолжу основной поток (или любой другой поток, использованный для вызова Camera.open()
до смены).
Итак, если вы начнете с кода
try {
mCamera = Camera.open(1);
}
catch (RuntimeException e) {
Log.e(LOG_TAG, "failed to open front camera");
}
// some code that uses mCamera immediately
сначала извлеките его как есть в приватный метод:
private void oldOpenCamera() {
try {
mCamera = Camera.open(1);
}
catch (RuntimeException e) {
Log.e(LOG_TAG, "failed to open front camera");
}
}
и вместо звонка oldOpenCamera()
просто используйте 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");
}
}
}
Обратите внимание, что весь обмен информацией между потоками notify () - wait() не требуется, если вы сразу не открываете mCamera в исходном коде.
Обновление: здесь тот же самый подход применяется к акселерометру: Датчик Акклерометра в Отдельной Нити
Вот ссылка на исходный код для HandlerThread и Looper.
Если вы посмотрите на два, вы увидите, что HandlerThread
это именно то, что он говорит - это удобный способ начать Thread
это имеет Looper
, Почему это существует? Поскольку потоки по умолчанию не имеют цикла сообщений. HandlerThread
это просто простой способ создать тот, который делает. Не могли бы вы продублировать эту функцию Handler
, Thread
, а также Looper
- судя по исходному коду - ответ - да.
Executor
это отличается. Executor
принимает представленные выполняемые задачи и - угадайте, что - выполняет их. Почему это необходимо? Это позволяет отделить выполнение задания от его фактического содержания. Когда бы вы использовали это? Скажем, у вас возникла ситуация, когда требовалось выполнить несколько задач одновременно. Вы можете выбрать, используя Executor
, чтобы запустить их все в одном потоке, чтобы они выполнялись серийно. Или вы можете использовать фиксированный пул потоков, чтобы некоторые, но не все, запускались одновременно. В любом случае суть задачи - то есть, что она на самом деле делает - отделена от способа ее выполнения.