我仍然对线程感到困惑。 我看到一些答案指向线程,UI线程,后台线程,主线程等(大多涉及AsyncTasks和从后台线程更新UI等)。是否有人可以给我一个完整的解释或至少一些链接?
如果答案涵盖以下情况,那就太好了:
涉及的所有线程:
- 当我运行活动时(设置内容视图,附加一些按钮,一些对话框消息)
- 具有AsyncTask的活动
- 后台服务
- HTTP调用
我仍然对线程感到困惑。 我看到一些答案指向线程,UI线程,后台线程,主线程等(大多涉及AsyncTasks和从后台线程更新UI等)。是否有人可以给我一个完整的解释或至少一些链接?
如果答案涵盖以下情况,那就太好了:
涉及的所有线程:
后台服务(service)服务默认在应用程序的进程中运行,在它自己的线程中运行。 (由@MisterSmith指出)服务默认在应用程序的进程中运行,在它自己的线程中运行。因此,您需要在服务中使用异步处理来在后台执行资源密集型任务。在应用程序进程中运行的服务有时被称为本地服务。但您可以指定在其自己的进程中运行服务:
将服务单独运行在其自己的进程中,以防止服务在主线程中执行长时间操作时阻塞应用程序。但由于服务在其自己的进程中运行,因此您需要使用一些进程间通信(IPC)来从其他部分与您的服务通信。
HttpClient
执行的HTTP调用(来自文档:HTTP客户端的线程安全性取决于特定客户端的实现和配置。
如果不想出现NetworkOnMainThreadException
,则必须在非UI线程上执行new Thread(new Runnable(...)).start();
。但是似乎HttpClient.execute()
方法在一个线程中执行,因此需要将处理程序作为参数之一传递以将结果传递到相应的处理程序(处理程序在UI线程上运行并且可以根据需要更新小部件(UI元素))。
额外信息:
要强制在主线程上执行某些操作,请使用yourContextInstance.runOnUiThread(new Runnable(....));
为了确定当前线程是否为UI(主)线程:
Looper.getMainLooper().getThread() == Thread.currentThread();
有关服务中线程的有趣问题:
为什么UI线程要负责所有这些?
由于 UI 线程负责将事件分派给适当的小部件(UI 元素),包括绘画事件。来源: this that 和一些 that 以及一些 that。当我运行一个活动(设置内容视图,附加一些按钮,一些对话框消息)时
通常只有一个线程(主线程,有时错误地称为UI线程)。
使用异步任务的活动
同样,默认情况下只有一个(主要的)线程。菜单和按钮处理程序也在主线程中运行。AsyncTask
则不同。它创建了一个工作线程(顺便提醒您在退出活动时小心不要泄漏它)。
后台服务
服务默认在主线程中运行。广播接收器也是如此。这就是为什么主线程不是(仅)UI线程的原因。某些类型的服务(例如流行的IntentService
)会生成工作线程。
http调用
是同步操作,因此会阻塞直到完成(这就是为什么您不应该在主线程中执行它的原因)。
WebView#loadUrl()
,那么该调用是异步执行的,并且可以在主线程上安全地进行。我知道你可以争论“http调用”是在后台线程上发生的事情,但在像这样的初学者问题的背景下,明确表述总是很好的。 - Nate从技术上讲,一个应用程序可以拥有任意数量的线程,它们由程序员任意创建。
然而,默认情况下,标准的Android应用程序只有一个线程。那就是主线程,通常被称为UI线程(因为它是唯一能够访问UI的线程)。默认情况下,所有操作都在主线程中进行。
如果您运行异步任务,则不同部分将在不同的线程中运行,以下是基本的分解:
onPreExecute()
在UI线程中运行,并且在执行异步任务时首先调用。
doInBackground()
在新生成的线程中运行,与主/UI线程分离。
onPostExecute()
在后台任务返回后在UI线程中运行。
后台服务完全独立于应用程序运行,即使原始应用程序被销毁,它也可以无限期地运行。
HTTP调用发生在您调用它的任何线程上,但作为较新的Android API的要求,您不能再从主/UI线程中执行任何网络活动。(这实际上会导致异常并终止您的应用程序)这是为了防止缓慢的网络调用阻塞主线程,从而创建一个不流畅的用户体验。
哪些线程参与其中:
当我运行一个活动(设置内容视图,附加一些按钮,一些对话框消息)时
这里涉及到UI线程或主线程。
带有AsyncTask的活动
涉及到UI线程和WorkerThread。
AsyncTask使得在UI线程中正确且容易地使用。该类允许您在不必操纵线程和/或处理程序的情况下,在后台执行操作并在UI线程上发布结果。
4个步骤
当异步任务被执行时,任务经历4个步骤:
onPreExecute()
,在任务执行之前在UI线程上调用。通常用于设置任务,例如在用户界面中显示进度条。
doInBackground(Params...)
,在onPreExecute()
完成后立即在后台线程上调用。此步骤用于执行可能需要很长时间的后台计算。此步骤还可以使用publishProgress(Progress...)
来发布一个或多个进度单位。这些值在UI线程上发布,在onProgressUpdate(Progress...)
步骤中。
onProgressUpdate
(Progress...),在调用publishProgress(Progress...)
后在UI线程上调用。此方法用于在后台计算仍在执行时在用户界面中显示任何形式的进度。
onPostExecute
(Result),在后台计算完成后在UI线程上调用。后台计算的结果作为参数传递给此步骤。
一个后台服务
IntentService类提供了一个简单的结构,用于在单个后台线程上运行操作。这使得它能够处理长时间运行的操作,而不影响用户界面的响应性。
一个HTTP调用
网络操作不能从UI线程(或主线程)执行。因此,请使用AsyncTask或HandlerThread等替代方案。
相关帖子: