Handler与AsyncTask的区别

127

我对何时选择AsyncTask和Handler感到困惑。比如说,我想每n秒运行一次某些代码,并更新UI,那么为什么要选择其中之一呢?


1
这个问题在使用AsyncTaskLoaders时变得更加复杂。请参考https://dev59.com/pWw05IYBdhLWcg3wpDRu获取更多信息。 - Warpzit
8个回答

75

在我看来,AsyncTask是为Android应用程序提供一种方便易用的方式来实现后台处理的,而不必太担心低层次的细节(线程、消息循环等)。它提供了回调方法来帮助安排任务,并且在需要时轻松更新UI。

然而,重要的是要注意,当使用AsyncTask时,开发人员要遵守其限制,这是由该类作者所做的设计决策导致的。例如,我最近发现,使用AsyncTasks可以安排的作业数量是有限制的。

Handler是两者中更透明的,可能会给你更多自由;因此,如果您想要更多的控制,您将选择Handler,否则AsynTask也可以正常工作。


没错。 我在项目中使用AsyncTasks来进行所有的后台操作。但是在某个时候,我开始达到了最大作业限制,因此我的任务只能在另一个任务完成后才能启动。最终,我不得不改变整个结构,停止使用asynctasks并避免达到那个限制。 - tbraun
5
从Honeycomb版本开始,AsyncTasks在一个线程上执行,所以不再有并行性。但您仍然可以在并行的Executor实现上运行它们。 - MrSnowflake

63

我的经验法则是:

  • 如果你正在做与UI相关的孤立任务,例如下载数据以在列表中显示,请使用 AsyncTask

  • 如果你正在进行多个重复任务,例如下载要在ImageViews中显示的多个图像(如下载缩略图),请使用带有Handler的任务队列。


处理程序在API级别1中,而ASYNCTASK在API级别3中。它们会被废弃吗?因为我正在专注于将应用程序从旧版本移植到2.2和2.3。 - yokks
10
两者都不会很快过时。由于用户界面基本上是围绕Handler构建的,因此Handler永远不会被弃用。 - alexanderblom
你不应该使用一个Loader来加载数据以供UI呈现吗? - nbarraille

20

尽可能避免使用AsyncTask,主要原因如下:

  • 由于系统设置了线程池的基础和最大大小,如果创建太多的AsyncTask,它们可能会被销毁。

  • 根据活动生命周期,即使在运行时,AsyncTask也可能被自动终止,并且您无法控制它。

  • 像onPostExecute这样在UI线程上运行的AsyncTask方法,可能会在Activity不再可见或可能处于不同的布局状态(例如方向更改后)时执行。

总之,不应使用与UIThread相关的AsyncTask方法,这是其主要优点!此外,您应该仅在doInBackground上执行非关键性工作。有关这些问题的更多见解,请阅读此讨论:

Is AsyncTask really conceptually flawed or am I just missing something?

总之,当以上任何问题可能成为问题时,请尽可能使用IntentServices、HandlerThread或ThreadPoolExecutor代替AsyncTask。这确实需要更多的工作,但您的应用程序将更加安全。


是的,我已经花了很多时间后悔使用AsyncTasks。它们看起来很棒,但是...问题太多了! - SMBiggs
6
很抱歉要这样强烈反对你的观点,但我不能让你糟糕的风格影响到 Android 初学者。第一点,你不应该运行那么多线程。如果你遇到这种情况,你的架构是有问题的。第二点,究竟如何做到的... 好吧,没错,在 Android 中,所有东西都可以被垃圾回收器回收... 只有在某些严格的任务滥用情况下,你才会看到上述描述的荒谬行为。第三点,管理你的任务,并不是说你很菜。你要么在 onPause() 调用时将其杀死,要么适当地分离和连接。 - StarWind0
1
这是你需要学习的所有内容,以便正确地执行任务而不会出现上述问题(假设你没有像喷发几百个任务之类的操作)。 - StarWind0

16

如果想每过 x 秒执行一次计算,你应该在 Handler 上安排一个 Runnable(使用 postDelayed()),并且这个 Runnable 应该在当前 UI 线程中开始。如果想要在另一个线程中启动它,可以使用 HandlerThread

AsyncTask 对我们来说更容易使用,但不如 handler 好用。


7
Handler与应用程序的主线程相关联,它处理和安排从后台线程发送到应用程序主线程的消息和运行。
AsyncTask提供了一种简单的方法来处理后台线程,以更新UI而不会通过耗时操作阻塞它。
答案是两者都可以用于从后台线程更新UI,不同之处在于您的执行场景。如果您想要延迟发布消息或以特定顺序将消息发送到MessageQueue,则可以考虑使用Handler。
如果您想要以一种简单方便的方式在应用程序主线程和后台线程之间交换参数(从而更新UI),则可以考虑使用AsyncTask。

3
您可以创建一个关联另一个线程的自定义处理程序。 - Aleksejs Mjaliks
Handler 不一定绑定到主线程(UI 线程)。它绑定到实例化它的线程,并处理到达该线程消息队列的 Message 或 Runnable。它还可以将 Message 和 Runnable 对象发送到该线程消息队列。 - azec-pdx

2
AsyncTask假定你会在UI线程上完成一些后台工作。另外,你只能执行它一次(之后,它的状态为FINISHED,如果再试图执行它,你将得到一个异常)。此外,它的灵活性不太大。是的,你可以使用THREAD_POOL_EXECUTOR进行并行执行,但这可能不值得努力。 Handler没有什么假定,除了处理Runnables和Messages。此外,你可以随意运行它多次。你可以自由决定它应该附加到哪个线程上,如何与其他处理程序通信,也许使用HandlerThread生成它们。因此,它更加灵活,适用于一些重复的工作。
在这里查看不同类型的Handler示例

0

这些是被问到的最好的面试问题。

AsyncTask - 它们用于在UI线程之外进行任务并在后台执行。

Handlers - Android没有直接的方法在UI和后台线程之间进行通信。必须使用Handlers通过消息队列发送消息或可运行对象。

因此,AsyncTasks用于需要在后台执行任务的情况,而Handlers用于UI和后台线程之间的通信。


0

doInBackground - 在另一个线程中执行工作。

onPostExecute - 将结果发布到UI线程,并且它会内部发送消息到主线程的处理程序。主UI线程已经有一个与之关联的looper和handler。

因此,如果您需要执行一些后台任务,请使用AsyncTask。但是最终,如果需要更新UI,则将使用主线程的处理程序。


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