深入认识AsyncTask

1、概述

Android开发中是采用单线程模型,主线程通常称为UI线程,由于UI线程的操作不是线程安全的,因此android规定有关更新界面的操作必须在主线程中进行,其他线程直接报错。

如果我们把所有操作都放在UI线程中也是不行的,因为这时候会出现ANR(Activity Not Respone),为了强化用户体验android在UI线程中不建议有超过5s的事件,当用户输入事件5s内没有得到响应(广播接收者的onReceive()执行时间超过10s)将弹出ANR对话框,因此一些耗时操作通常放在子线程中处理,这里通常使用AsyncTask或者Handler,本篇重点介绍前者,通常AsyncTask非常适合需要将中间值和最终结果返回给用户的情形。

2、如何使用AsyncTask

分为以下3步:

2.1、继承AsyncTask

AsyncTask是一个抽象类,我们在使用它之前需要继承AsyncTask类,有3个泛型参数(三个参数可以是任何类型),
第一个参数:传入doInBackground()方法的参数类型,本例是String类型的URL地址
第二个参数:传入onProgressUpdate()方法的参数类型,本例是Integer
第三个参数:传入onPostExecute()方法的参数类型,也是doInBackground()方法返回的类型。本例是Bitmap下载显示的图片

2.2、重写方法

       
    【onPreExecute()】该方法将在执行实际的后台操作前被UI thread调用。这个方法只是做一些准备工作,如在界面上显示一个进度条。

    【doInBackground(Params...)】, 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将只能负责执行那些很耗时的后台计算工作,不能进行UI操作。

    【publishProgress】 该方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
    【onProgressUpdate(Progress...)】  在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,可以通过一个进度条进行展示。

    【onPostExecute(Result)】, 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.,可以更新UI

可以借用一张图来表示他们之间的关系

2.3、在UI中使用

要注意以下几点:

       【1】 Task的实例必须在UI thread中创建
       【2】execute方法必须在UI thread中调用  

3、代码实例

使用AsyncTask在网上下载图片

布局很简单垂直的LinearLayout,放了一个button,progressbar,imageView,记得在配置清单文件加上网络访问权限,需要从网上下载图片,这里就不展示了,后面有demo下载

主要看下Mativity.Java,里面有详细注释

[java] view plain copy

  1. <span style="font-size:18px;">public class MainActivity extends AppCompatActivity {  
  2.     private ImageView image;  
  3.     private Button showButton;  
  4.     private ProgressBar mProgressBar;  
  5.     private int number;  
  6.     List<String> imageUrl;  
  7.   
  8.   
  9.     @Override  
  10.     protected void onCreate(Bundle savedInstanceState) {  
  11.         super.onCreate(savedInstanceState);  
  12.         setContentView(R.layout.activity_main);  
  13.         //布局控件  
  14.         image = (ImageView) findViewById(R.id.imagePic);  
  15.         showButton = (Button) findViewById(R.id.myButton);  
  16.         mProgressBar = (ProgressBar) findViewById(R.id.processbar);  
  17.         //button绑定监听器  
  18.         showButton.setOnClickListener(new showButtonListener());  
  19.         //添加下载图片的URL  
  20.         imageUrl = new ArrayList<String>();  
  21.         imageUrl.add("http://image.tianjimedia.com/uploadImages/2011/266/AIO90AV2508S.jpg");  
  22.         imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/063N2L5N2HID.jpg");  
  23.         imageUrl.add("http://comic.sinaimg.cn/2011/0824/U5237P1157DT20110824161051.jpg");  
  24.         imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/1429QO6389U8.jpg");  
  25.         imageUrl.add("http://new.aliyiyao.com/UpFiles/Image/2011/01/13/nc_129393721364387442.jpg");  
  26.   
  27.   
  28.     }  
  29.   
  30.   
  31.     public class showButtonListener implements View.OnClickListener {  
  32.   
  33.         @Override  
  34.         public void onClick(View v) {  
  35.   
  36.             MyAsyncTask myAsyncTask = new MyAsyncTask(getApplicationContext());  
  37.             myAsyncTask.execute(imageUrl.get(number % imageUrl.size()));//这样可以循环下载url图片  
  38.             number++;  
  39.   
  40.   
  41.         }  
  42.   
  43.   
  44.     }  
  45.   
  46.     //String:uri,Integer进度,Bitmap 图片  
  47.     private class MyAsyncTask extends AsyncTask<String, Integer, Bitmap> {  
  48.         public MyAsyncTask(Context context) {  
  49.             mProgressBar.setVisibility(View.VISIBLE);  
  50.             image.setVisibility(View.GONE);  
  51.         }  
  52.   
  53.         /* 
  54.             *onPreExecute() 该方法将在执行实际的后台操作前被UI thread调用。这个方法只是做一些准备工作, 
  55.             * 如在界面上显示一个进度条。 
  56.             *doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。 
  57.             * 这里将主要负责执行那些很耗时的后台计算工作。 
  58.             *publishProgress 该方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。 
  59.             *onProgressUpdate(Progress...), 在publishProgress方法被调用后, 
  60.             * UI thread将调用这个方法从而在界面上展示任务的进展情况,可以通过一个进度条进行展示。 
  61.             *onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用, 
  62.             * 后台的计算结果将通过该方法传递到UI thread. 
  63.           */  
  64.         @Override  
  65.         protected Bitmap doInBackground(String... params) {  
  66.             Bitmap bitmap = null;  
  67.             //根据输入的URL下载对应的图片  
  68.             try {  
  69.                 URL mUrl = new URL(params[0]);  
  70.                 URLConnection urlConnection = mUrl.openConnection();  
  71.                 urlConnection.connect();  
  72.                 InputStream inputStream = urlConnection.getInputStream();  
  73.                 bitmap = BitmapFactory.decodeStream(inputStream);  
  74.                 //  Toast.makeText(getApplicationContext(),"图片下载完成",Toast.LENGTH_LONG).show();该方法是非UI线程中,  
  75.                 // 如果加上这句会报错,体现了非UI线程不能更新UI界面  
  76.                 inputStream.close();  
  77.   
  78.   
  79.             } catch (MalformedURLException e) {  
  80.                 e.printStackTrace();  
  81.             } catch (IOException e) {  
  82.                 e.printStackTrace();  
  83.             }  
  84.             return bitmap;  
  85.         }  
  86.   
  87.         /* 
  88.         *在doInBackground方法执行完成后调用,onPreExecute方法被UI线程调用,后台的计算结 
  89.         *果通过该方法传递到UI线程 
  90.         * */  
  91.         @Override  
  92.         protected void onPostExecute(Bitmap bitmap) {  
  93.             mProgressBar.setVisibility(View.GONE);  
  94.             image.setVisibility(View.VISIBLE);  
  95.             if (bitmap != null) {  
  96.                 image.setImageBitmap(bitmap);  
  97.             } else {  
  98.                 Toast.makeText(getApplicationContext(), "网络异常", Toast.LENGTH_SHORT).show();  
  99.             }  
  100.         }  
  101.   
  102.         @Override  
  103.         protected void onPreExecute() {  
  104.             // 任务启动  
  105.             Toast.makeText(getApplicationContext(), "任务开始......", Toast.LENGTH_SHORT).show();  
  106.         }  
  107.     }  
  108. }  
  109. </span>  

4、AsyncTask的缺陷

【1】生命周期

AsyncTask不会随着Activity的销毁而销毁。它会一直 执行, 直到doInBackground()方法执行完毕。然后,如果 cancel(boolean)被调用, 那么onCancelled(Result result) 方法会被执行;否则,执行onPostExecute(Result result) 方法。如果我们的Activity销毁之前,没有取消 AsyncTask,这有可能让我们的AsyncTask崩溃(crash)。 另外,即使我们正确地调用了cancle() 也未必能真正地取消任务。因为如果在doInBackgroud里有一个不可中断的操作,比如BitmapFactory.decodeStream(),那么这个操作会继续下去。

【2】内存泄露

如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。

【3】并行还是串行

在Android 1.6之前的版本,AsyncTask是串行的,在1.6至2.3的版本,改成了并行的。在2.3之后的版本又做了修改,可以支持并行和串行,当想要串行 执行时,直接执行execute()方法,如果需要并行执行,则要执行executeOnExecutor(Executor)。

【4】结果丢失

 屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。

【5】薄弱的并发性能(大于128个线程直接FC)

AsyncTask类包含一个全局静态的线程池,线程池的配置参数如

[java] view plain copy

  1. private static final int CORE_POOL_SIZE =5;//5个核心工作线程  
  2.    private static final int MAXIMUM_POOL_SIZE = 128;//最多128个工作线程  
  3.    private static final int KEEP_ALIVE = 1;//空闲线程的超时时间为1秒  
  4.    
  5.    private static final BlockingQueue<Runnable> sWorkQueue =  
  6.            new LinkedBlockingQueue<Runnable>(10);//等待队列  
  7.    
  8.    private static final ThreadPoolExecutorsExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,  
  9.            MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue,sThreadFactory);//线程池是静态变量,所有的异步任务都会放到这个线程池的工作线程内执行。  

通过以上线程池参数可以知道
1、线程池中的工作线程少于5个时,将会创建新的工作线程执行异步任务
2、线程池中已经有5个线程,缓冲队列未满,异步任务将会放到缓冲队列中等待
3、线程池中已经有5个线程,缓冲队列已满,那么线程池将新开工作线程执行异步任务
4、线程池中已经有128个线程,缓冲队列已满,如果此时向线程提交任务,将会抛出RejectedExecutionException
如果我们使用超过它的范围直接就不行了
详细可以参看这篇http://blog.csdn.net/mylzc/article/details/6784415#

本练习dome地址http://download.csdn.net/detail/xsf50717/9194849

转载:http://blog.csdn.net/xsf50717/article/details/49255509

时间: 2024-08-30 16:37:25

深入认识AsyncTask的相关文章

对AsyncTask进行封装,简化繁琐的异步操作

对于android的异步操作,往往有二种方法给予我们选择.      一:Thread+Handler      二:  AsyncTask        对于二者的相同与异同点,这里并不去进行比较.这里提供一种简化异步操作的方法,供大家参考学习.         代码参考自AndEngine,不过我对它做了一定的修改,让它更适合自己目前项目的开发,感谢open source 能够让我们去学习和阅读那么多优秀的代码.        废话不多说,代码如下:        CallEarliest<

Android中用HandlerThread模拟AsyncTask功能(ThreadTask)

前言 AsyncTask是个好东西,能处理绝大多数应用线程和更新UI的任务,由于其内部使用了静态线程池,如果你有一堆异步任务(例如全局定时更新数据.同一个Activity中多个AsyncTask同时执行)其中有不能马上执行完的情况(例如网络请求超时),那就糟了,其他任务都还等着呢,就会出现任务卡住的情况.此时就需要直接上Thread了,这里参考AsyncTask的API封装了一个ThreadTask,便于必要时代码替换,欢迎交流! 农民伯伯: http://over140.cnblogs.com

Android AsyncTask与handler

本文主要讲解下AsyncTask的使用以及Handler的应用 首先,我们得明确下一个概念,什么是UI线程.顾名思义,ui线 程就是管理着用户界面的那个线程! android的ui线程操作并不是安全的,并且和用户直接进行界面交互的操作都必须在 ui线程中进行才可以.这种模式叫做单线程模式. 我们在单线程模式下编程一定要注意:不要阻塞ui线程.确保只在ui 线程中访问ui组件 当我们要执行一个复杂耗时的算法并且最终要将计算结果反映到ui上时,我们会发现,我们根本没办 法同时保证上面的两点要求:我们

Android AsyncTask异步处理

在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行. 在单线程模型中始终要记住两条法则: 1. 不要阻塞UI线程 2. 确保只在UI线程中访问Android UI工具包 当一个程序第 一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件 ,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理.所以主线程通常又被叫做UI

Android线程操作对象AsyncTask线程机制

 简单的说,一个程序只有一个主线程,可以有多个主线程.在Android世界中也是这样,Android属于单线程模型,耗时操作必须放在非主线程中执行,故而谷歌为了方便我们使用线程,为我们提供一个AsyncTask多线程操作对象.   对于Android使用线程还有一点需要特别注意,哪就是Android不允许在子线程中更新UI,相信很多初学者一定遇到过这个问题,这个怎么解决呢?在Activity中,我们可以通过   new Thread(new Runnable() { @Override publ

Android:多线程之AsyncTask

 AsyncTask AsyncTask,异步任务,可以简单进行异步操作,并把执行结果发布到UI主线程.AsyncTask是一个抽象类,它的内部其实也是结合了Thread和Handler来实现异步线程操作,但是它形成了一个通用线程框架,更清晰简单.AsyncTask应该被用于比较简短的操作(最多几秒钟).如果需要保持长时间运行的线程,可以使用ThreadPooExecutor或者FutureTask,关于这两个类的内容,以后再介绍,本片博客主要介绍AsyncTask. AsyncTask被定义为

解决如何让AsyncTask终止操作

 受到这个的启发终于结局了如何在AsyncTask运行中终止其操作. 单纯的onCancelled(true)是不行的 下面把代码贴出来~实现了登陆功能. AsyncTask简介,它使创建需要与用户界面交互的长时间运行的任务变得更简单.相对来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和Handler即可实现. package com.isummation.exampleapp; import java.io.BufferedReader; import java.io

Android 中文 API (101) —— AsyncTask

前言 本章内容是android.os.AsyncTask,版本为Android 2.3 r1,翻译来自"0_1",欢迎访问它的博客:"http://dev.10086.cn/blog/?32546",再次感谢"0_1" !期待你一起参与Android API的翻译,联系我over140@gmail.com.    声明 欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com/ Android中文翻译组:http

Android AsyncTask完全解析,带你从源码的角度彻底理解

我们都知道,Android UI是线程不安全的,如果想要在子线程里进行UI操作,就需要借助Android的异步消息处理机制.之前我也写过了一篇文章从源码层面分析了Android的异步消息处理机制,感兴趣的朋友可以参考 Android Handler.Message完全解析,带你从源码的角度彻底理解 . 不过为了更加方便我们在子线程中更新UI元素,Android从1.5版本就引入了一个AsyncTask类,使用它就可以非常灵活方便地从子线程切换到UI线程,我们本篇文章的主角也就正是它了. Asyn

android-在哪个线程中运行 AsyncTask?

问题描述 在哪个线程中运行 AsyncTask? 应用程序的 AlertDialog.Builder(context) 中有下面的代码.问题是在哪个线程中运行呢? final Thread myPrettyOperation = new Thread() { @Override public void run() { //Do some really long operation. } }; class MyPrettyTask extends AsyncTask<Void, Integer,