我试图了解使用HandlerThread的最佳用例。

按照定义:


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


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

评论

我从未使用过HandlerThread,但也许这篇文章会有所帮助-stackoverflow.com/questions/7462098 / ...

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

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

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

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

#1 楼

这是一个实际的示例,其中HandlerThread变得很方便。注册摄像机预览框时,您会在onPreviewFrame()回调中收到它们。文档解释说,此回调是在从其调用open(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");
        }
    }
}


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

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

评论


那你要在哪里合上相机呢?

–马特西亚斯
2014年2月24日在8:13

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

– Alex Cohn
2014年2月24日13:56

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

–丰富
2014年3月23日在7:17

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

– Alex Cohn
2014年12月22日19:17

@Praveen:这是您可以在覆盖notify()时执行的操作

– Alex Cohn
2014-12-26 21:05

#2 楼

这是HandlerThread和Looper源代码的链接。

如果同时查看两者,您会发现HandlerThread正是它所说的-启动具有ThreadLooper的便捷方法。为什么会存在?因为线程默认情况下没有消息循环。 HandlerThread只是创建一个简单的方法。您能否使用HandlerThreadLooper复制此功能-从源代码来看-答案是肯定的。

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

评论


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

– Delyan
13年8月10日在13:58

我猜不清楚。我并不是要暗示执行者使任务执行更加容易。而是将执行程序的目的设计为仅处理那些任务的执行。 HandlerThread的用途不同。稍后再编辑。

–Rarw
13年8月10日在14:27