Лучшее использование 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, чтобы запустить их все в одном потоке, чтобы они выполнялись серийно. Или вы можете использовать фиксированный пул потоков, чтобы некоторые, но не все, запускались одновременно. В любом случае суть задачи - то есть, что она на самом деле делает - отделена от способа ее выполнения.

Другие вопросы по тегам