Android:何时应该使用Handler(),何时应该使用Thread()?

147
当我需要运行一些异步操作,例如长时间运行的任务或使用网络的逻辑时,或者出于其他原因,启动一个新线程并运行它就可以了。创建一个 Handler 并运行它也可以正常工作。那它们之间有什么区别?何时应该使用每个选项?使用 Handler 而不是 Thread 的优势/原因是什么?
注: - 为了这个问题,我们忽略 AsyncTask。 - 我已经清楚了 Handler().postDelayed 的用例,为了这个问题假设我需要任务立即开始。

在你的情况下,直接使用新线程即可,我的下一个建议是AsyncTask,但显然这不是你想要的。如果你想要为可运行对象添加延迟或其他类型的自定义,则大多数情况下会使用处理程序。 - kabuto178
1
@kabuto178 嗯,有其他值得一提的处理程序的好处被你忽略了。例如,可以从一个单独的线程与 UI 线程进行交互。 - tony9099
9个回答

186
如果你正在进行的操作比较“繁重”,应该在一个线程中完成。如果你没有显式地在自己的线程中启动它,那么它将在主(UI)线程上运行,这可能会使用户界面出现卡顿或响应缓慢的情况。
有趣的是,当你使用线程时,通常也会使用Handler作为工作线程和主线程之间的通信手段。
典型的线程/Handler交互可能如下所示:
Handler h = new Handler(){
    @Override
    public void handleMessage(Message msg){
        if(msg.what == 0){
            updateUI();
        }else{
            showErrorDialog();
        }
    }
};

Thread t = new Thread() {
    @Override
    public void run(){
        doSomeWork();
        if(succeed){
            //we can't update the UI from here so we'll signal our handler and it will do it for us.
            h.sendEmptyMessage(0);
        }else{
            h.sendEmptyMessage(1);
        }
    }   
};

总的来说,需要在进行一些可能会耗时或者计算量大的工作(例如网络操作、文件I/O、复杂算术等)时使用线程。


感谢您的快速回复和投入的时间(以及您的回复速度!!)!那么,让我看看我是否理解了:Handler旨在促进工作线程和UI线程之间的非阻塞通信? - JRun
3
是的,那是其中一种用途。请查看Java文档中关于Handler描述的内容,了解更多信息,包括另一种用途(安排消息和可运行程序在将来某个时间执行)。 - FoamyGuy
很好地解释了@FoamyGuy! - tony9099
嗨,updateUI()onCreateView之后运行(在新视图加载完成后)是否有保证? - Zyoo
多个线程可以向同一个Handler报告吗?如果可以,那么应该如何实现? - gonzobrains
1
为什么是 message.what()?难道不应该是 if(msg == 0){ 吗?非常感谢! :) - Ruchir Baronia

81

Handler和Thread是两个完全不同的东西。

必须创建一个线程来执行长时间运行的作业。

Handler是在两个线程之间进行通信的非常方便的对象(例如:后台线程需要更新UI。您可以使用一个Handler从后台线程向UI线程发布一些Runnable)。

因此,您不能选择Handler或Thread。使用线程来执行重型工作!(如果您的后台线程将触发另一个线程中要执行的作业,大多数时候会使用Handler - 通常是UI线程)


感谢您的快速回复和所花费的时间(以及您的回复速度!!)! - JRun

34

HandlerThread是两个不同的东西,但它们并不矛盾。你可以同时拥有一个Handler和一个Thread,实际上每个Handler必须在一个Thread中运行。

如果需要更多细节,您可以查看这篇文章

输入图像描述


21

Handler运行在同一个Thread上,而Thread则运行在不同的线程上。

如果需要在同一线程上运行某些东西(通常是GUI元素或类似物),请使用Handler。

如果要保持主线程空闲以执行其他任务,请使用Thread。适用于任何需要较长时间的操作。


6
如果我想在同一线程上运行某些东西,为什么要使用 Handler?mHandler.post(...) 方法的目的是什么? - Elias
1
在这种情况下,Elias,如果你想让某个任务在一定时间后运行,或者每隔X段时间重复执行某个任务,那么你可以使用handler。如果你不想使用这些东西,那么你是正确的。没有必要使用handler。因为你已经在UI线程中了,所以你可以直接在那里做GUI的事情,因为handler在创建它的线程上运行。 - tony9099

17

Handler是在后台线程和UI线程之间进行通信的最佳方式。通常,Handler与一个线程的消息队列相关联,用于向消息队列发送消息和可运行对象。

用途:

线程:在独立的(后台)线程中执行任务,而不是在UI线程中执行。(有助于解除UI线程的阻塞)

Handler:用于在UI线程和后台线程之间进行通信。

请查看此文章


4
如果您需要从新线程更新用户界面,则需要与用户界面线程同步。您可以使用android.os.Handler类或AsyncTasks类来实现这一点。Handler类可以更新用户界面。Handler提供了接收Message或Runnable类实例的方法。您的线程可以通过sendMessage(Message msg)方法或sendEmptyMessage()方法发布消息。...更多关于线程等的信息在此处(包括不同线程和同步机制的教程以及何时使用何种)。

感谢您抽出时间回答我的问题。我喜欢Lars Vogel的博客,它非常有深度且易于理解。谢谢! - JRun

2
使用Handler而不是Thread的优点/原因是什么? Handler允许您发送和处理与线程的消息队列相关联的Message和Runnable对象。每个Handler实例与单个线程及其消息队列相关联。
当您创建一个新的Handler时,它绑定到创建它的线程/消息队列--从那时起,它将向该消息队列传递消息和运行,并在它们从消息队列中出来时执行它们。
有两个主要用途的Handler:
1.调度消息和Runnables在将来某个时间点执行 2.将操作排队以在不同于您自己的线程上执行。
如果使用Java线程,则必须自行处理一些事情--与主线程同步,取消线程等。
这个单线程不会创建线程池,除非您使用 ThreadPoolExecutorExecutorService API。

(从您在 Blackbelt 答案评论中提取的此查询)

为什么不使用 Executor?即使我确实想使用 Handler 来执行该操作,该怎么做?

参考文献:Thread Performance article

有些工作可以简化为高度并行的分布式任务。由于这会产生大量的工作包,因此 AsyncTaskHandlerThread 不是合适的类。 AsyncTask 的单线程特性会将所有线程池化的工作转换为线性系统。另一方面,使用 HandlerThread 类需要程序员手动管理一组线程之间的负载平衡。

ThreadPoolExecutor是一个辅助类,可以使这个过程更加容易。该类管理线程组的创建、设置它们的优先级以及管理工作在这些线程之间的分配方式。随着工作负载的增加或减少,该类会启动或销毁更多的线程来调整工作负载。
 BlockingQueue workQueue= new LinkedBlockingQueue<Runnable>(100); // Work pool size
 ThreadPoolExecutor executor = new ThreadPoolExecutor(
            Runtime.getRuntime().availableProcessors(),       // Initial pool size
            Runtime.getRuntime().availableProcessors(),       // Max pool size
            1, // KEEP_ALIVE_TIME
            TimeUnit.SECONDS, //  KEEP_ALIVE_TIME_UNIT
            workQueue);

您可以参考开发者指南文章create-threadpool获取更多细节。
查看此帖子以了解使用Handler运行多个Runnable实例的用法。在这种情况下,所有Runnable任务将在单个线程中运行。 Android:在线程中使用Toast

1

Handler 可以与 Thread 结合使用,以创建队列机制。您可以使用 handler 将某些内容发布到 ThreadLooper 上。


感谢您抽出时间回答我的问题。为什么不使用Executor?即使我想使用Handler来实现,应该怎么做? - JRun
executor 有点不同。为了使用它,您必须扩展 thread 并在 run 中调用 Looper 类的 static.方法 prepare。调用静态方法循环后,将创建一个队列,您可以使用 handlerbin 来转发请求并获取结果。 - Blackbelt

0

如果您需要在主线程之外单独执行一次任务,则使用Thread。如果您想重复执行任务,则Android提供了一种保持线程活动并接收消息或Runnable对象以使用MessageQueue处理它们的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接