android Handler vs AsyncTask vs Thread
如果你看看AsyncTask
和Handler
的源代码,你会看到他们的代码纯粹是用Java编写的。 (当然,也有一些例外,但这不是重点。)
所以在AsyncTask
或Handler
没有魔法。 作为开发人员,他们只是让你的工作更轻松。
例如:如果程序A调用方法A(),则方法A()可以与程序A在不同的线程中运行。您可以使用以下方法轻松验证它:
Thread t = Thread.currentThread();
int id = t.getId();
为什么你应该使用新的线程? 你可以谷歌的。 许多原因。
那么, Thread
, AsyncTask
和Handler
之间的区别是什么?
AsyncTask
和Handler
是用Java编写的(在内部它们使用一个Thread
),所以您可以使用Handler
或AsyncTask
执行所有AsyncTask
,您也可以使用Thread
来实现。
Handler
和AsyncTask
真的可以帮助你吗?
最明显的原因是调用者线程和工作线程之间的通信。 ( 调用者线程 :调用Worker线程来执行某个任务的线程,调用者线程不一定是UI线程)。 当然,您可以通过其他方式在两个线程之间进行通信,但由于线程安全问题,存在很多缺点(和危险)。
这就是为什么你应该使用Handler
和AsyncTask
。 他们为你做了大部分工作,你只需要知道要覆盖哪些方法。
Handler
和AsyncTask
之间的区别是:当调用者线程是UI线程时使用AsyncTask
。 这是android文档所说的:
AsyncTask支持正确和简单地使用UI线程。 该类允许执行后台操作并在UI线程上发布结果,而无需操纵线程和/或处理程序
我想强调两点:
1)易于使用UI线程(所以,当调用者线程是UI线程时使用)。
2)不需要操作处理程序。 (意思是:你可以使用Handler而不是AsyncTask,但AsyncTask是一个更简单的选项)。
这篇文章中有很多东西我还没有说过,例如:什么是UI线程,或者为什么它更容易。 你必须知道每种背后的一些方法并使用它,你会完全理解为什么。
@:当你阅读Android文档时,你会看到:
Handler允许您发送和处理与线程的MessageQueue关联的Message和Runnable对象
起初他们可能看起来很奇怪。 只要理解每个线程都有每个消息队列(就像一个待办事项列表),线程就会接收每条消息并执行,直到消息队列为空(就像完成工作并上床睡觉一样)。 所以,当Handler
通信时,它只是给调用者线程发送消息,并等待处理。 复杂? 请记住, Handler
可以以安全的方式与调用者线程进行通信。
我对Android中的Handlers
, AsyncTask
和Threads
之间的差异略有困惑。 我在这里阅读了很多博客和问题。
Handler
是后台线程,可让您与UI进行通信。 例如更新进度条应通过Handler
完成。 使用处理程序,您具有MessagingQueues
的优势,所以如果您想要安排消息或更新多个UI元素或者有重复任务。
AsyncTask
是相似的,事实上它们使用Handler
,但不在UI线程中运行,因此它适用于获取数据,例如获取Web服务。 稍后您可以与UI进行交互。
但是Thread
不能与UI交互,提供更多的“基本”线程,并且你错过了AsyncTask
所有抽象。
不过,我想要在服务中运行套接字连接。 应该在一个处理程序或线程中运行,还是在AsyncTask
? UI交互不是必需的。 它在我使用的性能方面有所不同吗?
同时, documentation得到了重大改进。
AsyncTask
用于执行一些后台计算并将结果发布到UI线程(具有可选的进度更新)。 既然你不关心用户界面,那么Handler
或Thread
似乎更合适。
你可以产生一个后台Thread
并通过使用Handler
的post
方法将消息传递回你的主线程。
在我看来,线程并不是执行套接字连接的最有效方式,但它们确实提供了运行线程方面最多的功能。 我说,因为从经验来看,长时间运行线程会导致设备非常热和资源密集。 即使是一个简单的while(true)
会在几分钟内加热手机。 如果你说UI交互不重要,也许一个AsyncTask
是好的,因为它们是为长期过程而设计的。 这只是我的看法。
UPDATE
请忽略我的上面的答案! 我在2011年回答了这个问题,当时我对Android的经验远不及现在。 我上面的答案是误导性的,被认为是错误的。 我要离开它,因为很多人在纠正我的意见后对它进行了评论,并且我吸取了教训。
在这个线程上还有其他更好的答案,但我至少会给我更正确的答案。 使用常规Java Thread
没有任何问题; 然而,你应该真的小心你的实现方式,因为做错了可能是处理器密集型的(最显着的症状可能是你的设备正在升温)。 AsyncTask
对于你想在后台运行的大多数任务非常理想(常见的例子是磁盘I / O,网络调用和数据库调用)。 但是, AsyncTask
不应该用于在用户关闭应用程序或将其设备置于待机状态后可能需要继续的特别长的进程。 我会说在大多数情况下,任何不属于UI线程的东西都可以在AsyncTask
。
Thread
:
您可以使用新Thread
进行长时间运行的后台任务,而不会影响UI线程。 从Java线程,你不能更新UI线程。
由于普通的Thread对Android架构没有多大用处,因此引入了线程的辅助类。
您可以在线程性能文档页面找到您的查询的答案。
Handler :
Handler
允许您发送和处理与线程的MessageQueue
关联的Message和Runnable
对象。 每个Handler
实例都与单个线程和该线程的消息队列相关联。
处理程序有两个主要用途:
安排消息和可运行子程序在将来的某个点执行;
排队一个动作,以便在不同于你自己的线程上执行。
AsyncTask
支持正确和简单地使用UI线程。 该类允许您执行后台操作并在UI线程上发布结果,而无需操作线程和/或处理程序。
缺点:
默认情况下,应用程序将其创建的所有
AsyncTask
对象推送到单个线程中。 因此,它们以串行方式执行,并且与主线程一样,特别长的工作包可以阻塞队列。 由于这个原因,使用AsyncTask处理持续时间短于5ms的工作项目。AsyncTask
对象也是隐式引用问题的最常见犯罪者。AsyncTask
对象也存在与明确引用相关的风险。
您可能需要一种更传统的方法来在较长时间运行的线程上执行一个工作块( 与AsyncTask不同,它应该用于5ms工作负载 ),并且有一些手动管理该工作流的能力。 处理程序线程实际上是一个长时间运行的线程,可以从队列中抓取工作,并对其进行操作。
这个类管理一组线程的创建,设置它们的优先级,并管理这些线程之间的工作分配方式。 随着工作负载的增加或减少,该类会加速或破坏更多线程以适应工作负载。
如果工作负载更大并且单个HandlerThread
不够用,则可以使用ThreadPoolExecutor
不过,我想要在服务中运行套接字连接。 应该在一个处理程序或线程中运行,还是在AsyncTask中运行? UI交互不是必需的。 它在我使用的性能方面有所不同吗?
由于UI交互不是必需的,您可能不会使用AsyncTask
。 普通线程没有多大用处,因此HandlerThread
是最佳选择。 由于您必须维护套接字连接,主线程上的Handler根本没有用处。 创建一个HandlerThread
并从HandlerThread
获取一个Handler
。
HandlerThread handlerThread = new HandlerThread("SocketOperation");
handlerThread.start();
Handler requestHandler = new Handler(handlerThread.getLooper());
requestHandler.post(myRunnable); // where myRunnable is your Runnable object.
如果你想回到UI线程,你可以使用一个更多的处理程序来处理响应。
final Handler responseHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
//txtView.setText((String) msg.obj);
Toast.makeText(MainActivity.this,
"Foreground task is completed:"+(String)msg.obj,
Toast.LENGTH_LONG)
.show();
}
};
在你的Runnable
,你可以添加
responseHandler.sendMessage(msg);
有关实现的更多细节可以在这里找到:
public class RequestHandler {
public String sendPostRequest(String requestURL,
HashMap<String, String> postDataParams) {
URL url;
StringBuilder sb = new StringBuilder();
try {
url = new URL(requestURL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setReadTimeout(15000);
conn.setConnectTimeout(15000);
conn.setRequestMethod("POST");
conn.setDoInput(true);
conn.setDoOutput(true);
OutputStream os = conn.getOutputStream();
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(os, "UTF-8"));
writer.write(getPostDataString(postDataParams));
writer.flush();
writer.close();
os.close();
int responseCode = conn.getResponseCode();
if (responseCode == HttpsURLConnection.HTTP_OK) {
BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
sb = new StringBuilder();
String response;
while ((response = br.readLine()) != null){
sb.append(response);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return sb.toString();
}
private String getPostDataString(HashMap<String, String> params) throws UnsupportedEncodingException {
StringBuilder result = new StringBuilder();
boolean first = true;
for (Map.Entry<String, String> entry : params.entrySet()) {
if (first)
first = false;
else
result.append("&");
result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
result.append("=");
result.append(URLEncoder.encode(entry.getValue(), "UTF-8"));
}
return result.toString();
}
}
线
当你启动一个应用程序时,会创建一个进程来执行代码。 为了有效地使用计算资源,可以在流程内启动线程,以便当时可以执行多个任务。 所以线程允许你通过有效利用cpu而不需要空闲时间来构建高效的应用程序。
在Android中,所有组件都在一个被调用的主线程上执行。 Android系统队列任务并在主线程上逐个执行它们。 当执行长时间运行的任务时,应用程序变得无响应。
为了防止这种情况,您可以创建工作线程并运行后台或长时间运行的任务。
处理器
由于android使用单线程模型,UI组件被创建为非线程安全的,这意味着只有它创建的线程才能访问它们,这意味着UI组件应该只在主线程上更新。 当UI组件在主线程上运行时,在工作线程上运行的任务不能修改UI组件。 这是Handler进入画面的地方。 处理程序在Looper的帮助下可以连接到新线程或现有线程,并运行连接线程中包含的代码。
处理程序使得可以进行线程间通信。 使用Handler,后台线程可以向其发送结果,并且连接到主线程的处理程序可以更新主线程上的UI组件。
的AsyncTask
由android提供的AsyncTask使用线程和处理程序在后台运行简单的任务,并将后台线程的结果更新为主线程。
有关示例,请参阅android线程,处理程序,asynctask和线程池 。