handler asynctaskk 的异步机制和普通线程有什么区别,分别有哪些优缺点

当前访客身份:游客 [
跟随自己的内心。
:非常感谢!!很详细很有用!!
:哥们 我转载了哈
:这个点了,感谢感谢。。
:good article
:if (getSupportActionBar() != null) { 3
:引用来自“lihywh”的评论我有个疑问,是不是会同...
:引用来自“Neavo”的评论 Phonegap官方并不推荐这...
:引用来自“act262”的评论 如果安装有多个市场或...
今日访问:5
昨日访问:90
本周访问:1022
本月访问:3528
所有访问:68573
多线程异步处理:AsyncTask异步更新UI界面(详细完整总结篇)
发表于3年前( 16:17)&&
阅读(2446)&|&评论()
0人收藏此文章,
转载自:&,在原先的基础上整理项目并重新发布。
AsyncTask的内部实现是一个线程池,每个后台任务会提交到线程池中的线程执行,然后使用Thread+Handler的方式调用回调函数。
AsyncTask抽象出后台线程运行的五个状态,分别是:1、准备运行,2、正在后台运行,3、进度更新,4、完成后台任务,5、取消任务,对于这五个阶段,AsyncTask提供了五个回调方法:
1、准备运行:onPreExecute(),该回调方法在任务被执行之后立即由UI线程调用。这个步骤通常用来建立任务,在UI上显示进度条。
2、正在后台运行:doInBackground(Params...),该回调方法由后台线程在onPreExecute()方法执行结束后立即调用。通常在这里执行耗时的后台计算,计算的结果必须由该方法返回,并被传递到onPostExecute()中。在该方法内也可使用publishProgress(Progress...)来发布一个或多个进度单位(units of progress),这些值将会在onProgressUpdate(Progress...)中被发布到UI线程。
3. 进度更新:onProgressUpdate(Progress...),该方法由UI线程在publishProgress(Progress...)方法调用完后被调用,一般用于动态地显示一个进度条。
4. 完成后台任务:onPostExecute(Result),当后台计算结束后调用。后台计算的结果会被作为参数传递给该方法。
5、取消任务:onCancelled (),在调用AsyncTask的cancel()方法时调用
AsyncTask的构造函数有三个模板参数:
1.Params:传递给后台任务的参数类型。
2.Progress:后台计算执行过程中,进步单位(progress units)的类型(就是后台程序已经执行了百分之几了)。
3.Result:后台执行返回的结果的类型。
AsyncTask并不总是需要使用上面的全部3种类型。标识不使用的类型很简单,只须用Void类型即可。
项目分析:
1、获取网络图片
点击按钮,模拟一个耗时动作,从网站下载图片并显示,取消按钮模拟取消操作,将进度条的值置零,并且换背景图片。
完整代码:
package com.xsjayz.
import org.apache.http.HttpR
import org.apache.http.client.HttpC
import org.apache.http.client.methods.HttpG
import org.apache.http.impl.client.DefaultHttpC
import android.app.A
import android.graphics.B
import android.graphics.BitmapF
import android.os.AsyncT
import android.os.B
import android.view.V
import android.view.View.OnClickL
import android.widget.B
import android.widget.ImageV
import android.widget.ProgressB
import android.widget.T
* AsyncTask
* @version
public class AsyncTaskActivity extends Activity {
private final static String IMAGE_PATH = &http://www.oschina.net/img/logo.gif&;
private ImageView imageV
private Button button2;
private ProgressBar progressB
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
imageView = (ImageView) findViewById(R.id.imageView);
button = (Button) findViewById(R.id.button);
button2 = (Button) findViewById(R.id.button2);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyTask myTask = new MyTask();
myTask.execute(IMAGE_PATH);
button2.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
MyTask myTask = new MyTask();
myTask.cancel(true);
* 继承AsyncTask
class MyTask extends AsyncTask&String, Integer, Bitmap& {
* 准备运行
// 该回调方法在任务被执行之后立即由UI线程调用。这个步骤通常用来建立任务,在UI上显示进度条。
protected void onPreExecute() {
imageView.setImageBitmap(null);// 每次在准备阶段先清除掉设置的图片
progressBar.setProgress(0);// 进度条初始值0
* 正在后台运行
// 该回调方法由后台线程在onPreExecute()方法执行结束后立即调用。
// 通常在这里执行耗时的后台计算,计算的结果必须由该函数返回,并被传递到onPostExecute()中。
// 在该方法内也可使用publishProgress()来发布一个或多个进度单位,这些值将会在onProgressUpdate()中被发布到UI线程。
protected Bitmap doInBackground(String... params) {
// 发布进度单位,系统将会调用onProgressUpdate()方法更新这些值。
publishProgress(0);// 进度值0
HttpClient httpClient = new DefaultHttpClient();
// 获取网站图片
HttpGet httpGet = new HttpGet(params[0]);
publishProgress(30);// 进度值30
HttpResponse httpResponse = httpClient.execute(httpGet);
publishProgress(70);// 进度值70
bitmap = BitmapFactory.decodeStream(httpResponse.getEntity()
.getContent());
} catch (Exception e) {
publishProgress(100);// 进度值100
* 进度更新
// 该方法由UI线程在publishProgress()方法调用完后被调用,一般用于动态地显示一个进度条。
protected void onProgressUpdate(Integer... progress) {
// 更新进度条的进度
progressBar.setProgress(progress[0]);
* 完成后台任务
// 后台任务执行完之后被调用,在UI线程执行。
protected void onPostExecute(Bitmap result) {
if (result != null) {
Toast.makeText(AsyncTaskActivity.this, &获取图片成功&,
Toast.LENGTH_LONG).show();
imageView.setImageBitmap(result);
Toast.makeText(AsyncTaskActivity.this, &获取图片失败&,
Toast.LENGTH_LONG).show();
* 取消任务
// 在调用AsyncTask的cancel()方法时调用,在UI线程执行。
protected void onCancelled() {
progressBar.setProgress(0);// 进度条复位
imageView.setImageDrawable(getResources().getDrawable(
R.drawable.ic_launcher));
Toast.makeText(AsyncTaskActivity.this, &取消从网络获取的图片&,
Toast.LENGTH_LONG).show();
布局文件:main.xml
&?xml version=&1.0& encoding=&utf-8&?&
&LinearLayout xmlns:android=&/apk/res/android&
android:layout_width=&fill_parent&
android:layout_height=&fill_parent&
android:orientation=&vertical& &
&ProgressBar
android:id=&@+id/progressBar&
style=&?android:attr/progressBarStyleHorizontal&
android:layout_width=&fill_parent&
android:layout_height=&wrap_content& &
&/ProgressBar&
&LinearLayout
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:layout_gravity=&center_horizontal&
android:orientation=&horizontal& &
android:id=&@+id/button&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:text=&@string/download_btn& /&
android:id=&@+id/button2&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content&
android:text=&@string/cancel_btn& /&
&/LinearLayout&
&ImageView
android:id=&@+id/imageView&
android:layout_width=&wrap_content&
android:layout_height=&wrap_content& /&
&/LinearLayout&
AsyncTask抽象出一个后台任务的五种状态,对应了五个回调接口,我们只需要根据不同的需求实现这五个接口(doInBackground是必须要实现的),就能完成一些简单的后台任务。
流程说明:
1、 &点击按钮时,创建一个MyTask,并且传入图片地址(String类型参数,因此AsyncTask的第一个模板参数为String类型)。
2、& UI线程执行onPreExecute(),把ImageView的图片清空,progrssbar的进度清零。
3、& 后台线程执行doInBackground(),不可以在doInBackground()操作UI,调用publishProgress(0)更新进度,此时会调用onProgressUpdate()更新进度条(进度用整形表示,因此AsyncTask的第二个模板参数是Integer)。方法最后返回result(例中返回Bitmap类型,因此AsyncTask的第二个模板参数是Bitmap)。
4、& 当后台任务执行完成后,调用onPostExecute(Result),传入的参数是doInBackground()中返回的对象。
1)">1)">1" ng-class="{current:{{currentPage==page}}}" ng-repeat="page in pages"><li class='page' ng-if="(endIndex<li class='page next' ng-if="(currentPage
相关文章阅读AsyncTask异步方式详解及其使用 - zuolongsnail的专栏
- 博客频道 - CSDN.NET
3422人阅读
在实际应用中经常会遇到比较耗时任务的处理,比如网络连接,数据库操作等情况时,如果这些操作都是放在主线程(UI线程)中,则会造成UI的假死现象,Android中可以使用AsyncTask和Handler两种异步方式来解决这种问题。
AsyncTask(异步任务处理)在使用AsyncTask时处理类需要继承AsyncTask,提供三个泛型参数,并且重载AsyncTask的四个方法(至少重载一个)。
三个泛型参数:1.Param 任务执行器需要的数据类型2.Progress 后台计算中使用的进度单位数据类型3.Result 后台计算返回结果的数据类型在设置参数时通常是这样的:String... params,这表示方法可以有0个或多个此类型参数;有时参数可以设置为不使用,用Void...即可。
四个方法:1.onPreExecute() 执行预处理,它运行于UI线程,可以为后台任务做一些准备工作,比如绘制一个进度条控件。2.doInBackground(Params...) 后台进程执行的具体计算在这里实现,doInBackground(Params...)是AsyncTask的关键,此方法必须重载。在这个方法内可以使用publishProgress(Progress...)改变当前的进度值。3.onProgressUpdate(Progress...) 运行于UI线程。如果在doInBackground(Params...) 中使用了publishProgress(Progress...),就会触发这个方法。在这里可以对进度条控件根据进度值做出具体的响应。4.onPostExecute(Result) 运行于UI线程,可以对后台任务的结果做出处理,结果就是doInBackground(Params...)的返回值。此方法也要经常重载,如果Result为null表明后台任务没有完成(被取消或者出现异常)。
示例代码如下,完整代码点下载。
// AsyncTask异步方式下载图片
class DownImageTask extends AsyncTask&String, Integer, Bitmap& {
// 执行预处理
protected void onPreExecute() {
super.onPreExecute();
// 显示进度条
progressBar.setVisibility(View.VISIBLE);
progressBar.setMax(100);
// 后台进程的执行
protected Bitmap doInBackground(String... params) {
URL url = new URL(params[0]);
HttpURLConnection conn = (HttpURLConnection) url
.openConnection();
InputStream inputStream = conn.getInputStream();
bitmap = BitmapFactory.decodeStream(inputStream);
// 进度条的更新,我这边只是用一个循环来示范,在实际应用中要使用已下载文件的大小和文件总大小的比例来更新
for (int i = 1; i &= 10; i++) {
publishProgress(i * 10);
Thread.sleep(200);
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
// 运行于UI线程,对后台任务的结果做出处理,doInBackground方法执行的结果作为此方法的参数
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
ImageView imageView = (ImageView) findViewById(R.id.image);
imageView.setImageBitmap(result);
progressBar.setVisibility(View.GONE);
// 运行于UI线程,如果在doInBackground(Params...)中使用了publishProgress(Progress...),就会触发此方法
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
progressBar.setProgress(values[0]);
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:546318次
积分:5488
积分:5488
排名:第1911名
原创:57篇
转载:14篇
评论:151条
(3)(2)(1)(3)(5)(4)(2)(4)(7)(3)(11)(14)(10)(2)Android实战技巧:多线程AsyncTask(版本不同,有点差异)
AsyncTask的介绍及基本使用方法
关于AsyncTask的介绍和基本使用方法可以参考和这里就不重复。
AsyncTask引发的一个问题
上周遇到了一个极其诡异的问题,一个小功能从网络上下载一个图片,然后放到ImageView中,是用AsyncTask来实现的,本身逻辑也很简单,仅是在doInBackground中用HTTP请求把图片的输入流取出,然后用BitmapFactory去解析,然后再把得到的Bitmap放到ImageView中。这个应用是用4.0的SDK开发的,也是运行在4.0上面的。但是有时候下载这张图片去要用很久很久,甚至要等上几分钟。通过调试发现一个令人难以接受的事实:竟然是doInBackground()未及时执行,也就是它并没有在#execute()调用之后马上执行,而是等待了很久才得以执行。
神马情况,难道AsyncTask不是线程,难道不是异步,难道AsyncTask另有内幕?
AsyncTask的内幕
AsyncTask主要有二个部分:一个是与主线各的交互,另一个就是线程的管理调度。虽然可能多个AsyncTask的子类的实例,但是AsyncTask的内部Handler和ThreadPoolExecutor都是进程范围内共享的,其都是static的,也即属于类的,类的属性的作用范围是CLASSPATH,因为一个进程一个VM,所以是AsyncTask控制着进程范围内所有的子类实例。
与主线程交互
与主线程交互是通过Handler来进行的,因为本文主要探讨AsyncTask在任务调度方面的,所以对于这部分不做细致介绍,感兴趣的朋友可以去看
线程任务的调度
内部会创建一个进程作用域的线程池来管理要运行的任务,也就就是说当你调用了AsyncTask#execute()后,AsyncTask会把任务交给线程池,由线程池来管理创建Thread和运行Therad。对于内部的线程池不同版本的Android的实现方式是不一样的:
Android2.3以前的版本,也即SDK/API 10和以前的版本
内部的线程池限制是5个,也就是说同时只能有5个线程运行,超过的线程只能等待,等待前面的线程某个执行完了才被调度和运行。换句话说,如果一个进程中的AsyncTask实例个数超过5个,那么假如前5个都运行很长时间的话,那么第6个只能等待机会了。这是AsyncTask的一个限制,而且对于2.3以前的版本无法解决。如果你的应用需要大量的后台线程去执行任务,那么你只能放弃使用AsyncTask,自己创建线程池来管理Thread,或者干脆不用线程池直接使用Thread也无妨。不得不说,虽然AsyncTask较Thread使用起来比较方便,但是它最多只能同时运行5个线程,这也大大局限了它的实力,你必须要小心的设计你的应用,错开使用AsyncTask的时间,尽力做到分时,或者保证数量不会大于5个,否则就可能遇到上面提到的问题。要不然就只能使用JavaSE中的API了。
Android 3.0以后,也即SDK/API 11和以后的版本
可能是Google意识到了AsyncTask的局限性了,从Android
3.0开始对AsyncTask的API做出了一些调整:
#execute()提交的任务,按先后顺序每次只运行一个
也就是说它是按提交的次序,每次只启动一个线程执行一个任务,完成之后再执行第二个任务,也就是相当于只有一个后台线程在执行所提交的任务(Executors.newSingleThreadPool())。
新增了接口#executeOnExecutor()
这个接口允许开发者提供自定义的线程池来运行和调度Thread,如果你想让所有的任务都能并发同时运行,那就创建一个没有限制的线程池(Executors.newCachedThreadPool()),并提供给AsyncTask。这样这个AsyncTask实例就有了自己的线程池而不必使用AsyncTask默认的。
新增了二个预定义的线程池SERIAL_EXECUTOR和THREAD_POOL_EXECUTOR
其实THREAD_POOL_EXECUTOR并不是新增的,之前的就有,只不过之前(Android
2.3)它是AsyncTask私有的,未公开而已。THREAD_POOL_EXECUTOR是一个corePoolSize为5的线程池,也就是说最多只有5个线程同时运行,超过5个的就要等待。所以如果使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)就跟2.3版本的AsyncTask.execute()效果是一样的。
而SERIAL_EXECUTOR是新增的,它的作用是保证任务执行的顺序,也就是它可以保证提交的任务确实是按照先后顺序执行的。它的内部有一个队列用来保存所提交的任务,保证当前只运行一个,这样就可以保证任务是完全按照顺序执行的,默认的execute()使用的就是这个,也就是executeOnExecutor(AsyncTask.SERIAL_EXECUTOR)与execute()是一样的。
前面问题的解法
了解了AsyncTask的内幕就知道了前面问题的原因:因为是4.0平台,所以所有的AsyncTask并不都会运行在单独的线程中,而是被SERIAL_EXECUTOR顺序的使用线程执行。因为应用中可能还有其他地方使用AsyncTask,所以到网络取图片的AsyncTask也许会等待到其他任务都完成时才得以执行而不是调用executor()之后马上执行。
那么解决方法其实很简单,要么直接使用Thread,要么创建一个单独的线程池(Executors.newCachedThreadPool())。或者最简单的解法就是使用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR),这样起码不用等到前面的都结束了再执行。
AsyncTask的使用注意事项
曾建议使用AsyncTask而不是使用Thread,但是AsyncTask似乎又有它的限制,这就要根据具体的需求情况而选择合适的工具,No
Silver Bullet。下面是一些建议:
改善你的设计,少用异步处理
线程的开销是非常大的,同时异步处理也容易出错,难调试,难维护,所以改善你的设计,尽可能的少用异步。对于一般性的数据库查询,少量的I/O操作是没有必要启动线程的。
与主线程有交互时用AsyncTask,否则就用Thread
AsyncTask被设计出来的目的就是为了满足Android的特殊需求:非主线程不能操作(UI)组件,所以AsyncTask扩展Thread增强了与主线程的交互的能力。如果你的应用没有与主线程交互,那么就直接使用Thread就好了。
当有需要大量线程执行任务时,一定要创建线程池
线程的开销是非常大的,特别是创建一个新线程,否则就不必设计线程池之类的工具了。当需要大量线程执行任务时,一定要创建线程池,无论是使用AsyncTask还是Thread,因为使用AsyncTask它内部的线程池有数量限制,可能无法满足需求;使用Thread更是要线程池来管理,避免虚拟机创建大量的线程。比如从网络上批量下载图片,你不想一个一个的下,或者5个5个的下载,那么就创建一个CorePoolSize为10或者20的线程池,每次10个或者20个这样的下载,即满足了速度,又不至于耗费无用的性能开销去无限制的创建线程。
对于想要立即开始执行的异步任务,要么直接使用Thread,要么单独创建线程池提供给AsyncTask
默认的AsyncTask不一定会立即执行你的任务,除非你提供给他一个单独的线程池。如果不与主线程交互,直接创建一个Thread就可以了,虽然创建线程开销比较大,但如果这不是批量操作就没有问题。
Android的开发没有想像中那样简单,要多花心思和时间在代码上和测试上面,以确信程序是优质的
附上相关资源:
使用自定义的CorePoolSize为7的Executor(Executors.newFixedThreadPool(7)):
使用未设限制的Executor(Executors.newCachedThreadPool()):
已投稿到:
以上网友发言只代表其个人观点,不代表新浪网的观点或立场。本身游戏有个主线程逻辑 我使用了handler 以及 AsyncTask 但是因为这两个其实是多线程的异步操作,android有没有单线程下的异步操作?比如联网的时候等候时间较长 导致无法响应UI线程的按键操作等- -!
问题补充:我用过了~但是这些实际上内部处理的时候是单开一个内部额外线程么?
问题补充:Ihavegotyou 写道可以的。
final Runnable myLongTimeJob = new Runnable() {
&&&&&&& public void run() {
&&&&&&&&&&&&&&&&&&&&&&&&&& someMethodHere();
&&&&&&&&&&&&&&&&&& }
};
new Thread() {
&&&&&&&&&&& public void run() {
&&&&&&&&&&&
_messageHandler.post(myLongTimeJob);
&&&&&&&&&&& }
&&&&&&& }.start();
AsyncTask也是可以的,
执行UI更新要在onPostExecute中作。
我用过了~但是这些实际上内部处理的时候是单开一个内部额外线程么?
问题补充:Ihavegotyou 写道上述的执行都在UI Thread(控制好取数据的先后逻辑,基本不会出现UI不能响应), 如果想要更好的效率就单独作Service吧。
如果在view里面 设置 一个Thread 用于更新逻辑,那个这个Thread是否还是属于UI呢?
public class XXView extends SurfaceView implements Runnable{
&& public surfaceCreated ( SurfaceHolder holder ) {
&&
&&&&&&&&&&&&&&&&& thread = new Thread(this);
thread.start();
&& }
&& public void run(){
&&&&& while(true){
&&&&&&&&& // 这个线程属于UI线程么?这个用于更新逻辑 比如联网什么的
&&&&& }
&& }
}
如果在这一个Thread 既保证联网不受阻塞同时也能响应触摸按键 的话 是不是就必需用另一个线程处理handel或是AsyncTask?
采纳的答案
引用如果在这一个Thread 既保证联网不受阻塞同时也能响应触摸按键 的话 是不是就必需用另一个线程处理handel或是AsyncTask?
Android的框架结构是不允许非UI Thread去更新相关Thread的,只能使用Handler或AsyncTask.
Android的框架结构是不允许非UI Thread去更新相关View的,只能使用Handler或AsyncTask.
&& 楼上正解,因为android本身就是单线程模型,启动的UI线程即是我们的主线程,而ui的更新等等就只能在主线程中操作,此时的Handler配合Thread就可以很好的达到阻塞效果。
&& 如果是网络请求等耗时操作,建议最好使用AsyncTask进行处理,因为它有更好的控制性,例如可以进行该Task的手动取消,相对而言,线程就不是那么好控制了。
&& 另外,关于内存消耗,AsyncTask要比普通线程消耗更多。
&& 希望对楼主有所帮助。
上述的执行都在UI Thread(控制好取数据的先后逻辑,基本不会出现UI不能响应), 如果想要更好的效率就单独作Service吧。
final Runnable myLongTimeJob = new Runnable() {
&&&&&&& public void run() {
&&&&&&&&&&&&&&&&&&&&&&&&&& someMethodHere();
&&&&&&&&&&&&&&&&&& }
};
new Thread() {
&&&&&&&&&&& public void run() {
&&&&&&&&&&&
_messageHandler.post(myLongTimeJob);
&&&&&&&&&&& }
&&&&&&& }.start();
AsyncTask也是可以的,
执行UI更新要在onPostExecute中作。
已解决问题
未解决问题}

我要回帖

更多关于 asynctask cancel 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信