Android:RunOnUiThread与AsyncTask的区别

36
Great! How can I assist you in translating text? What language do you need to translate from and to?
    // some code #1
    Thread t = new Thread("Thread1") {
        @Override
        public void run() {
            // some code #2
            runOnUiThread(new Runnable() {
                public void run() {
                    // some code #3 (that needs to be ran in UI thread)

                }
            });

        }
    };
    t.start();

vs.

AsyncTask:

onPreExecute() {
   // some code #1
}

doInBackground() {
   // some code #2
}

onPostExecute() {
   // some code #3
}

有哪些优缺点?

编辑:

我不想要像“代码更易于查看”,“方便开发者”等回答。 我实际上正在寻找幕后的技术差异。

例如,Paul Nikonowicz下面的答案就是我想看到的答案。(但AsyncTask行为相同)

5个回答

53

当你使用new Thread时,每次执行都会创建一个新的线程。然而,AsyncTask使用一个静态池最多可以容纳128个线程,并在存在旧线程时重复使用旧线程。因此,连续运行10次的AsyncTask只会创建一个线程,让该线程运行任务10次,而不是创建10个线程。

这是其中的一个区别,还有很多其他区别。


4
谢谢。我想听到的内容。如果您知道其他差异,我很想知道。此外,你是如何知道AsyncTask具有最大128个线程的静态池? - jclova
2
你是怎么知道AsyncTask有一个静态池,里面有128个线程的呢? - songyy
6
在这个链接中:https://github.com/android/platform_frameworks_base/blob/master/core/java/android/os/AsyncTask.java#L181 有这样一行注释:有5个核心线程始终在运行,最多可以有128个线程。如果存在超过5个线程,则如果它们闲置超过1秒钟,它们将被终止。 - zapl

13

这实际上是一种方便。 AsyncTask 框架处理管理 Thread 池并提供简单易懂的接口。对于熟悉如何使用 AsyncTask 的人来说,其 UI 活动可以在 onPreExecute()onPostExecute()onProgressUpdate() 中进行,而所有"繁重的工作"则在 doInBackground() 中完成,该方法无法触及 UI。

它使得开发者能够轻松地拥有一个简单的后台任务,可以轻松地向 UI 线程发布更新并在完成时返回结果。这不是魔术,只是一种便利。


说得再好不过了。 - Jean-Philippe Roy
我不能否认你从开发者方便的角度做了很好的解释。但是,我正在寻找技术上的差异。(例如哪一个更快、使用的内存更少等) - jclova
很好的解释。 - David Kariuki

5
根据这篇文章AsyncTask更易于使用,但存在以下限制:
  • 核心池大小和工作队列固定: 5个池/10个元素
    • 它是硬编码的,无法更改
  • 线程优先级固定为
  • 异常处理不如Thread受到支持

还有另一个差异我还没想明白。您可以查找和检查AsyncTask的完整源代码,因为Android是开源的 :-)

祝您在Android编码中愉快!


4
主要的缺点是使用自己的线程会让你的活动在调用完成时仍然保持活跃状态。Android将为您的所有线程提供时间来完成。这会导致活动泄漏的影响,最终将导致您的应用程序变得非常缓慢,直到您的应用程序被用户或操作系统强制退出。
您可以通过查看ADB中的进程来验证此内容。即使活动已经完成,您仍会看到它卡在那里占用资源。
因此,如果您使用自己的线程,请确保对其进行管理。或者,只需使用android api。选择权在您手中。

4
如果您没有在AsyncTask#cancel(boolean)上调用它且实现者未能妥善处理中断或及时检查AsyncTask#isCancelled(),则AsyncTask可能会在#onDestroy后继续执行。 - Jens
哦,真的吗?你应该编辑我的答案以获得积分 :) 很好知道!谢谢。 - Paul Nikonowicz
@Jens 我认为AsyncTask与其运行的活动绑定,在活动被销毁时它也会被销毁。 - IgorGanapolsky
3
不是这样的,但如果你实现得不好,它们很容易继续持有已经被销毁的Activity(对于长时间运行的任务,这意味着如果尝试更改视图等,会出现资源泄漏和其他问题)。 - Jens
@Jens是正确的,参考"http://blog.danlew.net/2014/06/21/the-hidden-pitfalls-of-asynctask/",虽然当屏幕旋转时活动被销毁和重建时Asynctask会受到影响,但在“AsyncTasks and the Lifecycle”部分中,它谈到了可能的内存问题。一直以来我都认为相反的情况,感谢Jens。 - TPG

2
这些差别非常大。
首先,所有用户交互和图形工作都在主线程上完成。
其次,AsyncTask是为短时间的专用活动而设计的,例如下载文件或上传数据。
第三,由于所有UI和用户交互都在主线程中完成,如果你开始向这个线程推送东西,设备将会出现滞后,并且对用户的命令反应不够灵敏。
唯一需要在UI线程中运行的原因是与小部件进行交互。除此之外,如果你想进行长时间的处理,请使用AsyncTask。
编辑:
你可以在一个单独的线程中下载你的数据,我对此没有任何问题。问题在于当你想要更新UI时。单独地,从子线程修改UI是不可能的。相反,你将不得不创建一个与UI线程绑定的处理程序,或者创建一个新线程,该线程注定要在UI线程中运行(如你的示例)。这不仅繁琐,而且浪费资源。AsyncTask可以简单高效地解决这个问题。
针对你最后一点,你是正确的。AsyncTask确实可以在pre/postExecute中访问主线程。然而,任务正在执行的处理(UI滞后的主要来源)却不能。使用Task,UI只会受到你所绘制的影响,而不必等待Task完成它的工作和任何它想要做的绘图。

1
我可能错了,但我不同意你的观点。
  • 我同意你的第一个观点。UI线程=主线程
  • 也可以使用“new thread()”方法实现文件下载。(除非你告诉我AsyncTask中还有其他内容)
  • AsyncTask的onPreExecute()和onPostExecute()也在UIThread(主线程)中运行。因此,如果你开始向该线程推送东西,设备也会出现延迟,并对用户命令的响应变得不那么敏感。
- jclova
关于“如果您想进行长时间处理,请使用AsyncTask。”严格来说,并不是长时间处理,只是中等处理(几秒钟)。请参见http://developer.android.com/reference/android/os/AsyncTask.html。 - ToolmakerSteve
@jclova 关于“onPreExecute和onPostExecute也在UIThread中运行...设备也会出现延迟”的评论,你想表达什么意思? AsyncTask 的整个目的是轻松地将触摸 UI 逻辑(在 onPreExecute、onProgressUpdate 和 onPostExecute 中)与不需要触摸 UI 的长时间运行逻辑(在 doInBackground 中)结合起来。显然,如果您将长时间运行的逻辑放在不应该放置的位置,那么就会引起问题 - 但是暗示这是 AsyncTask 的固有缺陷是错误的。 - ToolmakerSteve
@jclova...如果您创建了一个新线程,并且还需要影响UI,则必须使用不同的机制在UI线程上运行UI-touching逻辑。getActivity().runOnUIThread,或者通过Handler handler = new Handler(Looper.getMainLooper());与您创建的处理程序进行交互。就UI响应性而言,据我所知,结果将是相同的。 - ToolmakerSteve

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