与其他类似类相比,最好使用HandlerThread


67

我正在尝试了解using的最佳用例HandlerThread

根据定义:

“用于启动具有循环程序的新线程的便捷类。然后可以使用循环程序创建处理程序类。请注意,仍必须调用start()。”

我可能是错的,但类似的功能,我可以通过使用实现ThreadLooperHandler。那我HandlerThread什么时候应该使用?一个例子真的很有帮助。


我从来没有使用过HandlerThread,但也许这帖子将帮助- stackoverflow.com/questions/7462098/...
塔尔Kanel

1
@TalKanel:感谢您的答复。我已经检查了这篇文章,但它谈到了Executor相对于HandlerThread的好处,我完全同意。我的问题更像是使用HandlerThread的最佳用例是什么
Androidme,2013年

该帖子也解决了这个问题。执行程序更灵活,因为他们可以管理线程池。多线程-您需要使用执行程序。单线程-您可以使用HandlerThread。不确定是否重要,因为执行官可以同时处理这两种情况。据我了解,HandlerThread仅允许您在后台线程上创建处理程序-可以通过其他方式完成此操作。
Rarw

在这里查看我的评论-stackoverflow.com/questions/17897536/how-to-handle-tcp-data/…。总的来说,我更喜欢HandlerThreads,因为通过Messages实现了超级简单的同步。也就是说,这也可能是瓶颈。此外,没有什么阻止您将ExecutorService与HandlerThreads一起使用。我以前写过一篇,并不难。
德莱恩(Delyan)2013年

@Rarw:这实际上是我的问题,如果我们可以通过其他方式来做事情,那么创建另一个类需要做什么。我确信在特殊情况下有一些好处,我需要知道。
Androidme 2013年

Answers:


86

这是一个实际的示例,其中HandlerThread变得很方便。注册摄像机预览框时,会在onPreviewFrame()回调中收到它们。该文件解释说,此回调事件线程打开(INT)从被称为上调用

通常,这意味着将在主(UI)线程上调用回调。因此,当打开菜单,对动画进行动画处理或即使屏幕上打印了统计信息时,处理巨大像素阵列的任务也可能会卡住。

简单的解决方案是创建一个new HandlerThread()并委托Camera.open()给该线程(我通过进行了此操作post(Runnable),您无需实现Handler.Callback)。

请注意,使用Camera进行的所有其他工作都可以照常完成,而不必委托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");
        }
    }
}

请注意,如果您没有在打开原始代码后立即访问mCamera,则不需要整个notify() - wait()线程间通信。

更新:这里对加速度计采用了相同的方法:单独线程中的加速度计传感器


4
那你要在哪里合上相机呢?
Matthias 2014年

2
@Matthias:这是一个很好的问题。如果您的用例允许这样做,请首先释放摄像机Activity.onPause(),因为根据Activity生命周期,这是保证您的活动从系统收到的唯一回调。但是在某些情况下,可以onPause()之后使用相机。在这种情况下,您只需要几个位置即可在不同情况下保持优雅。
Alex Cohn 2014年

1
)}关闭新的Runnable应该是})
Rich

1
@Praveen当然,在这种情况下,“静态”没有什么神圣的
Alex Cohn

1
@Praveen:这是您可以改写的方法notify()
Alex Cohn

15

这是HandlerThreadLooper的源代码的链接。

如果您看一下两者,您会发现aHandlerThread正是它所说的-启动Thread具有a的a的便捷方法Looper。为什么会存在?因为线程默认情况下没有消息循环。这HandlerThread只是创建一个可以做到的简单方法。您是否可以使用HandlerThread和复制此函数Looper-从源代码来看-答案是肯定的。

一个Executor是不同的。一个Executor需要提交可运行的任务和-你猜怎么着-执行它们。为什么这是必要的?它使您可以将任务的执行与其实际内容脱钩。你什么时候用这个?假设您遇到需要同时执行多个任务的情况。您可以选择使用Executor来在单个线程上运行它们,以便按顺序执行它们。或者,您可以使用固定线程池,以使某些(但不是全部)同时运行。在这两种情况下,任务的实质(即任务的实际执行方式)与执行方式是分开的。


1
我认为您的区分是不准确的。您也可以通过Handler.postRunnable提交要执行的任务。实际上,您可以通过仅更改ThreadFactory和Submit()方法来创建仅使用HandlerThreads的ExecutorService。借助HandlerThreads,跨线程通信变得更加容易,而不是任务提交。
Delyan 2013年

1
我猜不清楚。我并不是要建议执行者使任务执行更容易。而是将执行程序的目的设计为仅处理那些任务的执行。HandlerThread具有不同的用途。稍后再编辑。
Rarw
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.