Handler vs AsyncTask vs Thread

我对Android中HandlersAsyncTaskThreads之间的差异感到有点困惑。我在StackOverflow上读过不少博客和问题。

Handler是后台线程,为您提供与UI通信。例如,更新进度条应该通过Handler来完成。使用处理器,你有MessagingQueues的优势,所以如果你想安排消息或更新多个UI元素或有重复的任务。

AsyncTask是类似的,事实上,他们使用Handler,但不运行在UI线程,所以它很适合获取数据,例如获取web服务。稍后您可以与UI交互。

然而,Thread不能与UI交互,提供更“基本”的线程,你错过了AsyncTask的所有抽象。

但是,我希望在服务中运行套接字连接。这应该在处理程序或线程中运行,甚至是AsyncTask?UI交互根本不需要。它对我使用的性能有影响吗?

同时,文档得到了很大的改进。

148139 次浏览

在我看来,线程并不是进行套接字连接的最有效的方式,但它们确实在运行线程方面提供了最多的功能。我这么说是因为根据经验,长时间运行线程会导致设备非常热且资源密集。即使是一个简单的while(true)也会在几分钟内加热手机。如果你说UI交互不重要,也许AsyncTask是好的,因为它们是为长期过程设计的。这只是我的看法。

更新

我在2011年回答了这个问题,当时我对Android的经验远不如现在。我上面的回答是误导性的,被认为是错误的。我把它留在那里,因为很多人在下面评论纠正我,我已经吸取了教训。

在这个帖子里还有其他更好的答案,但我至少会给我一个更合适的答案。使用常规的Java Thread;然而,你真的应该注意如何实现它,因为如果做错了,可能会非常消耗处理器(最显著的症状可能是你的设备加热)。__abc1对于你想在后台运行的大多数任务来说是非常理想的(常见的例子是磁盘I/O,网络调用和数据库调用)。然而,__abc1不应该用于特别长的进程,这些进程可能需要在用户关闭应用程序或将设备置为待机后继续运行。我想说,在大多数情况下,任何不属于UI线程的东西都可以在AsyncTask中处理。

AsyncTask用于执行一些后台计算并将结果发布到UI线程(带有可选的进度更新)。既然你不关心UI,那么HandlerThread似乎更合适。

你可以生成一个后台Thread并通过使用Handlerpost方法将消息传递回主线程。

如果我们查看源代码,会发现AsyncTaskHandler完全是用Java编写的。(不过也有一些例外。但这不是重点)

所以在AsyncTaskHandler中没有魔法。这些类使开发人员的工作更加轻松。

例如:如果程序A调用方法A(),方法A()可以在程序A的不同线程中运行。我们可以通过以下代码轻松验证:

Thread t = Thread.currentThread();
int id = t.getId();

为什么我们应该使用一个新线程的一些任务?你可以为它谷歌。很多很多原因,e。G:起重、长时间工作。

那么,ThreadAsyncTaskHandler之间有什么区别呢?

AsyncTaskHandler是用Java写的(在内部它们使用Thread),所以我们可以用HandlerAsyncTask做的所有事情,我们也可以用Thread来实现。

HandlerAsyncTask能真正帮助什么?

最明显的原因是调用线程和工作线程之间的通信。 调用者线程:调用工作线程来执行某些任务的线程。调用线程不一定是UI线程)。当然,我们可以用其他方式在两个线程之间进行通信,但由于线程安全,存在许多缺点(和危险)

这就是为什么我们应该使用HandlerAsyncTask。这些类为我们做了大部分工作,我们只需要知道要重写哪些方法。

HandlerAsyncTask之间的区别是:当调用者线程UI线程时使用AsyncTask。 这是android文档说的:

AsyncTask允许正确和容易地使用UI线程。这个类 允许执行后台操作,并在UI上发布结果 线程,而不必操作线程和/或处理器

我想强调两点:

1)易于使用UI线程(所以,当调用线程是UI线程时使用)。

2)不需要操纵处理程序。(意思是:你可以使用Handler而不是AsyncTask,但AsyncTask是一个更简单的选择)。

在这篇文章中有很多东西我还没有说,例如:什么是UI线程,或者为什么它更容易。你必须知道每个类背后的一些方法并使用它,你才会完全理解其中的原因。

@:当你阅读Android文档时,你会看到:

处理器允许您发送和处理消息和可运行对象 与线程的MessageQueue

关联

这种描述乍一看可能很奇怪。我们只需要了解每个线程都有每个消息队列(就像一个待办事项列表),并且线程将接收每条消息,直到消息队列为空(就像我们完成工作并上床睡觉一样)。因此,当Handler通信时,它只是给调用线程一个消息,然后等待处理。

复杂?只要记住Handler可以安全地与调用线程通信。

AsyncTask被设计为在后台执行不超过几秒的操作(不建议从服务器下载兆字节的文件或计算cpu密集型任务,如文件IO操作)。如果您需要执行一个长时间运行的操作,强烈建议您使用java本机线程。Java为您提供了各种与线程相关的类来完成您所需要的工作。使用Handler来更新UI线程。

线程

Android支持标准Java 线程。你可以使用标准线程和包" java.util.concurrent "中的工具把动作放到后台。唯一的限制是您不能直接从后台进程更新UI。

如果你需要从后台任务更新UI,你需要使用一些Android特定的类。你可以使用类" android.os.Handler "或者类" AsyncTask "

Handler

类" Handler "可以更新UI。句柄为接收消息和可运行对象提供方法。要使用处理程序,你必须子类化它并覆盖handleMessage()来处理消息。要处理Runable,你可以使用方法post();。在你的活动中只需要一个处理程序实例。

线程可以通过sendMessage(Message msg)sendEmptyMessage方法发布消息。

AsyncTask

如果你有一个Activity需要下载内容或执行可以在后台完成的操作,AsyncTask允许你维护一个响应式用户界面,并将这些操作的进度发布给用户。

要了解更多信息,你可以看看这些链接。

< a href = " http://mobisys。在/博客/ 2012/01 / android-threads-handlers-and-asynctask-tutorial / noreferrer“rel = > http://mobisys.in/blog/2012/01/android-threads-handlers-and-asynctask-tutorial/ < / >

http://www.slideshare.net/HoangNgoBuu/android-thread-handler-and-asynctask

深入观察之后,事情就一目了然了。

AsyncTask:

这是使用线程在不了解Java线程模型的情况下的简单方法。 AsyncTask给出了不同的工作线程和主线程的回调

用于小的等待操作,如下所示:

  1. 从web服务中获取一些数据并在布局中显示。
  2. 数据库查询。
  3. 当你意识到运行的操作永远不会被嵌套。

Handler:

当我们在android中安装一个应用程序时,它会为该应用程序创建一个名为MAIN UI thread的线程。所有活动都在该线程中运行。根据android单线程模型规则,我们不能访问UI元素(位图,textview等)直接为另一个线程内定义的活动。

Handler允许你从其他后台线程与UI线程通信。这在android中很有用,因为android不允许其他线程直接与UI线程通信。处理程序可以发送和处理与线程的MessageQueue关联的Message和Runnable对象。每个Handler实例都与单个线程和该线程的消息队列相关联。创建新的Handler时,它将绑定到创建它的线程的线程/消息队列。

它最适合:

  1. 它允许您进行消息排队。
  2. 消息调度。

Thread:

现在是讨论线程的时候了。

Thread是AsyncTaskHandler的父线程。它们都在内部使用线程,这意味着您还可以创建自己的线程模型就像AsyncTaskHandler,但这需要对Java的多线程实现有很好的了解。

让我试着用一个例子来回答这个问题:)- MyImageSearch[请参考这里的主活动屏幕的图像-包含编辑文本/搜索按钮/网格视图]

MyImageSearch

__abc0 - __abc1

__abc0 - __abc1

< p > 现在回答问题 - 因此,一旦我们获得了用于获取单个图像的JSON数据,我们就可以实现通过Handlers或线程或AsyncTask在后台获取图像的逻辑。我们应该注意到,由于我的图像一旦下载就必须显示在UI/主线程上,我们不能简单地使用线程,因为它们不能访问上下文。 在FlickrAdapter中,我能想到的选项是:

  • 选择1:创建一个LooperThread [extends thread] -然后继续 在一个线程中按顺序下载图像 李打开[looper.loop () < / >
  • 选择2:使用线程池并通过myHandler发布可运行对象 包含引用我的ImageView,但由于视图在网格视图 被回收,问题可能再次出现在索引4的图像在哪里 显示在索引9[下载可能需要更多时间]
  • 选择3[我使用这个]:使用线程池并向myHandler发送消息,其中包含与ImageView的索引和相关的数据 ImageView本身,所以在执行handleMessage()时,我们将更新 ImageView仅当currentIndex匹配Image的索引 try to download.
  • . txt
  • 选择4:使用AsyncTask下载 但在这里,我将无法访问我想要的线程数 在不同的android版本中,线程池的大小有所不同,但在选择3中,我可以根据正在使用的设备配置有意识地决定线程池的大小

以下是源代码:

public class MainActivity extends ActionBarActivity {


GridView imageGridView;
ArrayList<FlickrItem> items = new ArrayList<FlickrItem>();
FlickrAdapter adapter;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);


imageGridView = (GridView) findViewById(R.id.gridView1);
adapter = new FlickrAdapter(this, items);
imageGridView.setAdapter(adapter);
}


// To avoid a memory leak on configuration change making it a inner class
class FlickrDownloader extends AsyncTask<Void, Void, Void> {






@Override
protected Void doInBackground(Void... params) {
FlickrGetter getter = new FlickrGetter();


ArrayList<FlickrItem> newItems = getter.fetchItems();


// clear the existing array
items.clear();


// add the new items to the array
items.addAll(newItems);


// is this correct ? - Wrong rebuilding the list view and should not be done in background
//adapter.notifyDataSetChanged();


return null;
}


@Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);


adapter.notifyDataSetChanged();
}


}


public void search(View view) {
// get the flickr data
FlickrDownloader downloader = new FlickrDownloader();
downloader.execute();
}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}


@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}

我希望我的回答虽然很长,但将有助于理解一些更精细的细节。

正如Vogella网站上关于Android后台处理与处理程序,AsyncTask和加载器的教程所述:

Handler类可用于注册到线程,并提供一个简单的通道来将数据发送到该线程。

AsyncTask类封装了后台进程的创建和与主线程的同步。它还支持报告正在运行的任务的进度。

Thread基本上是多线程的核心元素,开发人员使用它有以下缺点:

如果你使用Java线程,你必须处理以下要求 在你自己的代码:

  • 如果将结果返回到用户界面,则与主线程同步
  • 没有取消线程的默认值
  • 没有默认线程池
  • Android中没有处理配置更改的默认值

关于AsyncTask,如Android开发者参考资料所述:

AsyncTask允许正确和容易地使用UI线程。这个类 允许执行后台操作,并在UI上发布结果

.线程,而不必操作线程和/或处理程序

AsyncTask被设计为围绕ThreadHandler的helper类 并且不构成通用的线程框架。asynctask 理想情况下应用于短时间的操作(几秒钟 大多数)。如果需要让线程长时间运行, 类提供的各种api是强烈推荐的 java.util.concurrent包,如Executor, ThreadPoolExecutor和 FutureTask . < / p >

2015年5月更新:我找到了一个精彩的系列讲座涵盖这个主题。

这是谷歌搜索:Douglas Schmidt讲座android并发和同步

这是YouTube上的第一课的视频

所有这些都是范德比尔特大学中的CS 282 (2013): Android系统编程的一部分。这里是YouTube播放列表

道格拉斯·施密特似乎是个出色的讲师

重要的是:如果你正在考虑使用AsyncTask来解决线程问题,你应该首先使用查看ReactiveX/RxAndroid来获得一个可能更合适的编程模式。通过实例学习RxJava 2 for Android是一个很好的获取概览的资源。

Handler -线程之间的通信媒介。在android中,它主要用于通过处理器创建和发送消息来与主线程通信

AsyncTask -用于在后台线程中执行长时间运行的应用程序。使用nAsyncTask,你可以在后台线程中执行操作,并在应用程序的主线程中获得结果。

Thread -是一个轻量级进程,以实现并发性和最大的cpu利用率。在android中,你可以使用线程来执行不触及应用程序UI的活动

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();
}


}

Thread:

你可以在不影响UI线程的情况下,将新的Thread用于长时间运行的后台任务。从java线程,你不能更新UI线程。

由于普通的线程对Android架构没有太大用处,因此引入了线程的帮助类。

您可以在线程的性能文档页中找到您查询的答案。

处理程序:

Handler允许你发送和处理Message和与线程的MessageQueue相关的Runnable对象。每个Handler实例都与单个线程和该线程的消息队列相关联。

Handler有两个主要用途:

  1. 将消息和可运行程序安排在未来的某个时间点执行;

  2. 将要在与自己的线程不同的线程上执行的操作排队。

AsyncTask:

AsyncTask使UI线程能够正确和简单地使用。该类允许您执行后台操作并在UI线程上发布结果,而无需操作线程和/或处理程序。

缺点:

  1. 默认情况下,应用程序将其创建的所有AsyncTask对象推入一个线程。因此,它们以串行方式执行,与主线程一样,一个特别长的工作包会阻塞队列。由于这个原因,使用AsyncTask来处理在持续时间中短于5ms的工作项。

  2. AsyncTask对象也是隐式引用问题最常见的违法者。AsyncTask对象也存在与显式引用相关的风险。

HandlerThread:

你可能需要一种更传统的方法来在长时间运行的线程(不像AsyncTask,它应该用于5ms的工作负载)上执行工作块,以及手动管理该工作流的能力。处理程序线程实际上是一个长时间运行的线程,它从队列中获取工作并对其进行操作。

ThreadPoolExecutor:

该类管理一组线程的创建,设置它们的优先级,并管理如何在这些线程之间分配工作。随着工作负载的增加或减少,该类将启动或销毁更多的线程以适应工作负载。

如果工作负载较多,且单个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线程通信,可以使用另外一个Handler来处理响应。

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);

关于实现的更多细节可以在这里找到:

Android: Toast in a thread .

线程

当你启动一个应用程序时,会创建一个进程来执行代码。为了有效地利用计算资源,可以在进程内启动线程,以便同时执行多个任务。所以线程允许你通过有效地利用cpu而没有空闲时间来构建高效的应用程序。

在Android中,所有组件都在一个称为主线程的线程上执行。Android系统将任务排队,并在主线程中逐个执行。当长时间运行的任务执行时,应用程序变得无响应。

为了防止这种情况,您可以创建工作线程并运行后台或长时间运行的任务。

处理程序

由于android使用单线程模型,UI组件是创建非线程安全的,这意味着只有它创建的线程应该访问它们,这意味着UI组件应该只在主线程上更新。由于UI组件运行在主线程上,运行在工作线程上的任务不能修改UI组件。这就是Handler出现的地方。Handler在Looper的帮助下可以连接到新线程或现有线程,并在连接的线程上运行它包含的代码。

处理器使线程间通信成为可能。后台线程可以通过Handler将结果发送给后台线程,连接到主线程的Handler可以更新主线程上的UI组件。

AsyncTask

AsyncTask提供的android使用线程和处理程序,使运行简单的任务在后台和更新结果从后台线程到主线程容易。

有关示例,请参见Android线程,处理器,异步任务和线程池

这取决于选择哪一个是基于需求的

处理程序主要用于从其他线程切换到主线程,Handler附加到一个循环器,它在队列中发布其可运行任务。 所以如果你已经在其他线程和切换到主线程,那么你需要处理而不是异步任务或其他线程

如果在主线程之外创建的Handler不是一个looper,则不会在线程创建Handler时给出错误,该线程需要被创建为lopper

AsyncTask用于在后台线程上执行几秒钟的代码,并将其结果提供给主线程 * * * AsyncTask的局限性 1. 异步任务不附加到活动的生命周期,即使它的活动被破坏,它也会继续运行,而加载器没有这个限制 2. 所有异步任务共享相同的后台线程执行,这也影响应用程序的性能

线程在app中也用于后台工作,但它在主线程上没有任何回调。 如果要求适合一些线程,而不是一个线程,需要给任务很多次,那么线程池执行器是更好的选择。Eg要求图像加载从多个url像glide