处理器VS AsyncTask与线程
我对Android中的Handlers
, AsyncTask
和Threads
之间的区别有些困惑。 我已经阅读了很多博客和问题在这里stackoverflow。
Handler
是后台线程,提供您与用户界面进行通信。 例如更新进度条应该通过Handler
来完成。 使用处理程序,您具有MessagingQueues
的优势,所以如果您想要安排消息或更新多个UI元素或具有重复任务。
AsyncTask
是相似的,事实上它们使用Handler
,但不会在UI线程中运行,所以它对于获取数据很有用,比如获取Web服务。 稍后,您可以与UI进行交互。
但是Thread
不能和UI交互,提供更多的“基本”线程,而且你会错过AsyncTask
所有抽象。
不过,我想有一个套接字连接运行在一个服务。 这应该运行在一个处理程序或线程,甚至是一个AsyncTask
? UI交互不是必须的。 它在我使用的性能方面有所不同吗?
同时文件已经大大改善。
作为Android背景处理教程, 使用 Vogella网站上的处理程序,AsyncTask和Loaders进行处理 :
Handler
类可以用来注册到一个线程,并提供一个简单的通道发送数据到这个线程。
AsyncTask
类封装了后台进程的创build以及与主线程的同步。 它还支持正在运行的任务的报告进度。
Thread
基本上是开发人员可以使用的multithreading的核心元素,具有以下缺点:
如果使用Java线程,则必须在自己的代码中处理以下要求:
- 如果将结果回传到用户界面,则与主线程同步
- 没有默认取消线程
- 没有默认的线程池
- 在Android中处理configuration更改时没有默认值
关于AsyncTask
,正如Android开发人员参考所指出的那样:
AsyncTask
支持正确和简单地使用UI线程。 这个类允许执行后台操作并在UI线程上发布结果,而不必操作线程和/或处理程序。
AsyncTask
被devise成围绕Thread
和Handler
的助手类,并不构成通用的线程框架。 理想情况下,AsyncTasks应该用于短操作(最多几秒钟)。如果你需要让线程长时间运行,强烈build议你使用java.util.concurrent包提供的各种API,比如执行器,ThreadPoolExecutor和FutureTask。
2015年5月更新:我发现了一系列涵盖这个主题的优秀系列讲座 。
这是Googlesearch: Douglas Schmidt讲课android的并发和同步
这是YouTube上第一场演讲的video
所有这些都是范德堡大学 CS 282(2013):Android系统编程的一部分 。 这里是YouTube播放列表
道格拉斯·施密特似乎是一位优秀的讲师
如果您查看AsyncTask
和Handler
的源代码,您将看到他们的代码纯粹是用Java编写的。 (当然,也有一些例外,但这不是重点。)
所以在AsyncTask
或Handler
没有魔法。 他们只是让你的工作更容易作为开发人员。
例如:如果程序A调用方法A(),则方法A()可以与程序A在不同的线程中运行。您可以使用以下方法轻松validation它:
Thread t = Thread.currentThread(); int id = t.getId();
为什么你应该使用一个新的线程? 你可以谷歌的。 许多原因。
那么, Thread
, AsyncTask
和Handler
什么区别呢?
AsyncTask
和Handler
是用Java编写的(在内部它们使用一个Thread
),所以你可以使用Handler
或者AsyncTask
来做任何事情,你也可以使用一个Thread
来实现。
Handler
和AsyncTask
可以帮助你什么?
最明显的原因是调用者线程和工作者线程之间的通信。 ( 调用者线程 :调用Worker线程执行某个任务的线程,调用者线程不一定是UI线程)。 当然,你可以用其他方式在两个线程之间进行通信,但是由于线程安全问题,还有很多缺点(和危险)。
这就是为什么你应该使用Handler
和AsyncTask
。 他们为你做大部分的工作,你只需要知道什么方法来覆盖。
Handler
和AsyncTask
的区别在于:当调用者线程是UI线程时,使用AsyncTask
。 这是android文档所说的:
AsyncTask支持正确和简单地使用UI线程。 该类允许执行后台操作并在UI线程上发布结果,而不必操作线程和/或处理程序
我想强调两点:
1)简单的使用UI线程(所以,当调用者线程是UI线程时使用)。
2)不需要操作处理程序。 (意思是:你可以使用Handler而不是AsyncTask,但AsyncTask是一个更简单的select)。
在这篇文章中还有很多东西,我还没有说,例如:什么是UI线程,或者为什么更容易。 你必须知道每种背后的一些方法,并使用它,你会完全明白为什么..
@:当你阅读Android文档时,你会看到:
Handler允许您发送和处理与线程的MessageQueue关联的Message和Runnable对象
起初他们可能看起来很奇怪。 只要理解每个线程都有每个消息队列(就像一个待办事项列表),线程就会把每条消息都做好,直到消息队列为空(就像完成工作,然后上床睡觉一样)。 所以,当Handler
通信时,它只是给调用者线程发送一条消息,并等待处理。 复杂? 请记住, Handler
可以以安全的方式与调用者线程进行通信。
深入研究之后,这很简单。
AsyncTask
:
这是一个简单的方法来使用一个线程, 而不知道任何关于Java线程模型 。 AsyncTask
给出了工作线程和主线程的各种callback。
用于如下的小型等待操作:
- 从Web服务提取一些数据并在布局上显示。
- 数据库查询。
- 当你意识到运行操作永远不会嵌套。
Handler
:
当我们在android中安装一个应用程序时,它会为该应用程序创build一个名为MAIN UI Thread的线程。 所有活动在该线程内运行。 通过android单线程模型规则,我们不能直接访问该活动中定义的另一个线程的UI元素(位图,文本视图等)。
处理程序允许您从其他后台线程与UI线程进行通信。 这在android中很有用,因为android不允许其他线程直接与UI线程通信。 处理程序可以发送和处理与线程的MessageQueue关联的消息和可运行对象。 每个Handler实例都与单个线程和该线程的消息队列相关联。 当一个新的Handler被创build时,它绑定到创build它的线程的线程/消息队列。
这是最适合的:
- 它允许你做消息排队。
- 消息调度。
Thread
:
现在是时候谈论线程了。
线程是AsyncTask
和Handler
的父类。 他们都在内部使用线程,这意味着您也可以创build自己的线程模型,如AsyncTask
和Handler
,但是这需要对Java的multithreading实现有很好的了解。
一个AsyncTask
用于做一些后台计算,并将结果发布到UI线程(带有可选的进度更新)。 既然你不关心用户界面,那么Handler
或Thread
似乎更合适。
您可以产生一个后台Thread
并通过使用Handler
的post
方法将消息传回给主线程。
在我看来,线程并不是执行套接字连接的最有效的方式,但它们提供了运行线程方面最多的function。 我说因为从经验来看,长时间运行线程会导致设备非常热,资源密集。 即使是一个简单的while(true)
,几分钟内就会加热一个电话。 如果您说UI交互不重要,那么AsyncTask
可能是好的,因为它们是为长期stream程devise的。 这只是我的意见。
UPDATE
请不理我的上面的答案! 我在2011年回答了这个问题,当时我对Android的经验远不及现在。 我上面的答案是误导性的,被认为是错误的。 我要把它留在那里,因为很多人在纠正我的意见后对它进行了评论,我也吸取了教训。
在这个线程上还有其他更好的答案,但我至less会给我更正确的答案。 使用普通的Java Thread
没有任何问题; 然而,你应该真的小心你如何实现它,因为做错了可能是非常处理器密集型(最显着的症状可能是你的设备升温)。 AsyncTask
对于你想要在后台运行的大多数任务来说都非常理想(常见的例子是磁盘I / O,networking调用和数据库调用)。 但是, AsyncTask
不应该用于特别长的进程,在用户closures应用程序或将其设备置于待机状态后可能需要继续执行。 我会说大多数情况下,任何不属于UI线程的东西都可以在AsyncTask
。
线
Android支持标准的Java 线程 。 您可以使用标准线程和“ java.util.concurrent
”包java.util.concurrent
的工具将操作置于后台。 唯一的限制是你不能直接从后台进程更新UI。
如果您需要从后台任务更新UI,则需要使用一些Android特定的类。 您可以使用类“ android.os.Handler
”为这个或类“ AsyncTask
”
处理器
类“ Handler
”可以更新UI。 一个句柄提供了接收消息和可运行的方法。 要使用处理程序,您必须对其进行子类化并重写handleMessage()
以处理消息。 要处理Runable
,可以使用post();
方法post();
您的活动中只需要一个处理程序实例。
你的线程可以通过sendMessage(Message msg)
或sendEmptyMessage
方法发布消息。
的AsyncTask
如果您有一个需要下载内容或执行后台操作的Activity
, AsyncTask
允许您维护一个响应式用户界面并向用户发布这些操作的进度。
欲了解更多信息,你可以看看这些链接。
http://mobisys.in/blog/2012/01/android-threads-handlers-and-asynctask-tutorial/
http://www.slideshare.net/HoangNgoBuu/android-thread-handler-and-asynctask
AsyncTask
被devise为在后台执行不超过几秒的操作(不build议从服务器下载数兆字节的文件或计算cpu密集型任务,如文件IO操作)。 如果您需要执行长时间运行的操作,则强烈build议您使用Java本机线程。 Java给你各种线程相关的类来做你所需要的。 使用Handler
程序更新UI线程。
让我试着用一个例子来回答这个问题:) – MyImageSearch [请参考主要活动屏幕的图像 – 包含一个编辑文本/searchbutton/网格视图]
MyImageSearch的描述 – 一旦用户在编辑文本框中input详细信息并点击searchbutton,我们将通过flickr提供的networking服务在互联网上search图像(您只需在那里注册以获得密钥/秘密标记) – 为了进行search,我们发送一个HTTP请求和GET JSON数据作为响应,包含单个图像的URL,然后我们将使用它加载网格视图。
我的实现 – 在主要活动中,我将定义一个内部类,它扩展了AsyncTask以在doInBackGround方法中发送HTTP请求,并获取JSON响应并更新我的本地ArrayList的FlickrItems,我将用它来更新我的GridView通过FlickrAdapter (扩展BaseAdapter)并调用AsyncTask的onPostExecute()中的adapter.notifyDataSetChanged()以重新加载网格视图。 请注意,这里的HTTP请求是一个阻塞调用,因为我已经通过AsyncTask完成了它。 而且,我可以在适配器中caching项目以提高性能或将它们存储在SDCard上。 我将在FlickrAdapter中膨胀的网格在我的实现中包含一个进度条和图像视图。 下面你可以find我使用的mainActivity的代码。
现在回答问题 – 因此,一旦我们有了用于获取单个图像的JSON数据,我们就可以实现通过Handlers或Threads或AsyncTask获取背景图像的逻辑。 在这里我们应该注意到,由于我的图像一旦下载,必须显示在UI /主线程上,我们不能简单地使用线程,因为它们无法访问上下文。 在FlickrAdapter中,我可以想到的select是:
- select1:创build一个LooperThread [extends线程] – 并继续下载图像顺序在一个线程中保持此线程打开[looper.loop()]
- select2:利用线程池并通过包含对ImageView的引用的myHandler发布runnable,但由于Grid View中的视图被回收,可能会出现索引为4时显示索引9处的图像的问题[可以下载花更多的时间]
- select3 [我使用这个]:使用线程池并发送消息给myHandler,它包含与ImageView的索引和ImageView本身有关的数据,所以在执行handleMessage()时,只有当currentIndex匹配我们试图下载的图像。
- select4:利用AsyncTask在后台下载图像,但是在这里我不会访问线程池中我想要的线程数量,它随着不同的Android版本而变化,但是在select3中我可以做出有意识的决定线程池的大小取决于正在使用的设备configuration。
这里的源代码:
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); } }
我希望我的回答能够帮助你理解一些细节。
Thread
:
您可以使用新Thread
进行长时间运行的后台任务,而不会影响UI线程。 从Java线程,你不能更新UI线程。
由于正常的线程对于Android架构没有多大用处,因此引入了线程的辅助类。
您可以在线程性能文档页面find您的查询的答案。
处理程序 :
Handler
允许您发送和处理与线程的MessageQueue
关联的Message和Runnable
对象。 每个Handler
实例都与单个线程和该线程的消息队列相关联。
处理程序有两个主要用途:
-
将日程安排消息和可运行程序作为一些要点;
-
排队要在不同于自己的线程上执行的操作。
AsyncTask :
AsyncTask
支持正确和简单地使用UI线程。 该类允许您执行后台操作并在UI线程上发布结果,而不必操作线程和/或处理程序。
缺点:
-
默认情况下,应用程序将其创build的所有
AsyncTask
对象推送到单个线程中。 因此,它们以串行方式执行,并且与主线程一样,特别长的工作包可以阻塞队列。 由于这个原因,使用AsyncTask来处理持续时间短于5ms的工作项目。 -
AsyncTask
对象也是隐式引用问题的最常见的犯罪者。AsyncTask
对象也存在与显式引用有关的风险。
HandlerThread :
您可能需要更传统的方法来在较长时间运行的线程上执行一个工作块( 不像AsyncTask,它应该用于5ms的工作负载 ),还有一些手动pipe理工作stream的能力。 处理程序线程实际上是一个长时间运行的线程,它从队列中抓取工作,并对其进行操作。
ThreadPoolExecutor :
这个类pipe理一组线程的创build,设置它们的优先级,并pipe理这些线程之间的工作分配方式。 随着工作量的增加或减less,这个类会加速或破坏更多的线程以适应工作负载。
如果工作负载更多,而且单个HandlerThread
还不够,则可以使用ThreadPoolExecutor
不过,我想有一个套接字连接运行在一个服务。 这应该运行在一个处理程序或线程,甚至是一个AsyncTask? UI交互不是必须的。 它在我使用的性能方面有所不同吗?
由于UI交互不是必需的,你可能不会去AsyncTask
。 普通线程没有多大用处,因此HandlerThread
是最好的select。 由于你必须保持套接字连接,主线程上的Handler根本没有用处。 创build一个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);
有关实现的更多细节可以在这里find:
Android:在一个线程中吐司
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(); } }
Handler
– 是线程之间的通信媒介。 在android中,它主要用于通过处理程序创build和发送消息来与主线程通信
AsyncTask
– 用于在后台线程中执行长时间运行的应用程序。 使用n AsyncTask
您可以在后台线程中执行操作,并在应用程序的主线程中获取结果。
Thread
– 是一个轻量级的过程,实现并发和最大的CPU利用率。 在android中,您可以使用线程来执行不会触摸应用程序UI的活动