Лучшее использование HandlerThread над другими подобными классами

Я пытаюсь понять наилучший вариант использования HandlerThread.

Согласно определению:

"Удобный класс для запуска нового потока, в котором есть петлитель. Затем можно использовать петлитель для создания классов обработчиков. Обратите внимание, что start() еще нужно вызвать.

Я могу ошибаться, но аналогичную функциональность я могу достичь, используя Thread, Looper и Handler. Итак, когда следует использовать HandlerThread? Пример был бы очень полезен.

Ответ 1

Вот пример реальной жизни, когда 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

сначала извлеките его as is в частный метод:

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 в исходном коде сразу после его открытия.

Обновление: Здесь тот же подход применяется к акселерометру: Датчик акселерометра в отдельной теме

Ответ 2

Вот ссылка на исходный код HandlerThread и Looper.

Если вы посмотрите на два, вы увидите, что HandlerThread - это именно то, что он говорит - удобный способ запустить Thread с Looper. Почему это существует? Поскольку потоки, по умолчанию не имеют цикла сообщений. HandlerThread - это просто простой способ создать тот, который делает. Не могли бы вы дублировать эту функцию с помощью Handler, Thread и Looper - судя по исходному коду - ответ да.

An Executor отличается. Executor принимает поставленные runnable задачи и - угадывает, что -executes их. Почему это необходимо? Это позволяет отделить выполнение задачи от ее фактического вещества. Когда вы это используете? Предположим, что у вас была ситуация, требующая одновременного выполнения нескольких задач. Вы можете выбрать, используя Executor, чтобы запустить их все в одном потоке, чтобы они выполнялись серийно. Или вы можете использовать фиксированный пул потоков, чтобы некоторые, но не все, запускались одновременно. В любом случае суть задачи - то есть то, что она на самом деле делает, - отделяется от того, как она выполняется.