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,里面有详细注释

<span style="font-size:18px;">public class MainActivity extends AppCompatActivity {
    private ImageView image;
    private Button showButton;
    private ProgressBar mProgressBar;
    private int number;
    List<String> imageUrl;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //布局控件
        image = (ImageView) findViewById(R.id.imagePic);
        showButton = (Button) findViewById(R.id.myButton);
        mProgressBar = (ProgressBar) findViewById(R.id.processbar);
        //button绑定监听器
        showButton.setOnClickListener(new showButtonListener());
        //添加下载图片的URL
        imageUrl = new ArrayList<String>();
        imageUrl.add("http://image.tianjimedia.com/uploadImages/2011/266/AIO90AV2508S.jpg");
        imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/063N2L5N2HID.jpg");
        imageUrl.add("http://comic.sinaimg.cn/2011/0824/U5237P1157DT20110824161051.jpg");
        imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/1429QO6389U8.jpg");
        imageUrl.add("http://new.aliyiyao.com/UpFiles/Image/2011/01/13/nc_129393721364387442.jpg");

    }

    public class showButtonListener implements View.OnClickListener {

        @Override
        public void onClick(View v) {

            MyAsyncTask myAsyncTask = new MyAsyncTask(getApplicationContext());
            myAsyncTask.execute(imageUrl.get(number % imageUrl.size()));//这样可以循环下载url图片
            number++;

        }

    }

    //String:uri,Integer进度,Bitmap 图片
    private class MyAsyncTask extends AsyncTask<String, Integer, Bitmap> {
        public MyAsyncTask(Context context) {
            mProgressBar.setVisibility(View.VISIBLE);
            image.setVisibility(View.GONE);
        }

        /*
            *onPreExecute() 该方法将在执行实际的后台操作前被UI thread调用。这个方法只是做一些准备工作,
            * 如在界面上显示一个进度条。
            *doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。
            * 这里将主要负责执行那些很耗时的后台计算工作。
            *publishProgress 该方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
            *onProgressUpdate(Progress...), 在publishProgress方法被调用后,
            * UI thread将调用这个方法从而在界面上展示任务的进展情况,可以通过一个进度条进行展示。
            *onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,
            * 后台的计算结果将通过该方法传递到UI thread.
          */
        @Override
        protected Bitmap doInBackground(String... params) {
            Bitmap bitmap = null;
            //根据输入的URL下载对应的图片
            try {
                URL mUrl = new URL(params[0]);
                URLConnection urlConnection = mUrl.openConnection();
                urlConnection.connect();
                InputStream inputStream = urlConnection.getInputStream();
                bitmap = BitmapFactory.decodeStream(inputStream);
                //  Toast.makeText(getApplicationContext(),"图片下载完成",Toast.LENGTH_LONG).show();该方法是非UI线程中,
                // 如果加上这句会报错,体现了非UI线程不能更新UI界面
                inputStream.close();

            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return bitmap;
        }

        /*
        *在doInBackground方法执行完成后调用,onPreExecute方法被UI线程调用,后台的计算结
        *果通过该方法传递到UI线程
        * */
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            mProgressBar.setVisibility(View.GONE);
            image.setVisibility(View.VISIBLE);
            if (bitmap != null) {
                image.setImageBitmap(bitmap);
            } else {
                Toast.makeText(getApplicationContext(), "网络异常", Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        protected void onPreExecute() {
            // 任务启动
            Toast.makeText(getApplicationContext(), "任务开始......", Toast.LENGTH_SHORT).show();
        }
    }
}
</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类包含一个全局静态的线程池,线程池的配置参数如

private static final int CORE_POOL_SIZE =5;//5个核心工作线程
   private static final int MAXIMUM_POOL_SIZE = 128;//最多128个工作线程
   private static final int KEEP_ALIVE = 1;//空闲线程的超时时间为1秒

   private static final BlockingQueue<Runnable> sWorkQueue =
           new LinkedBlockingQueue<Runnable>(10);//等待队列

   private static final ThreadPoolExecutorsExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
           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

深入认识AsyncTask的更多相关文章

  1. Android 旋转屏幕--处理Activity与AsyncTask的最佳解决方案

    一.概述 运行时变更就是设备在运行时发生变化(例如屏幕旋转.键盘可用性及语言).发生这些变化,Android会重启Activity,这时就需要保存activity的状态及与activity相关的任务, ...

  2. Android笔记——AsyncTask介绍

    AsyncTask和Handler对比 1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操 ...

  3. Android线程管理之AsyncTask异步任务

    前言: 前面几篇文章主要学习了线程以及线程池的创建与使用,今天来学习一下AsyncTask异步任务,学习下AsyncTask到底解决了什么问题?然而它有什么弊端?正所谓知己知彼百战百胜嘛! 线程管理相 ...

  4. Android中使用AsyncTask实现文件下载以及进度更新提示

    Android提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单.相对Handler来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和 ...

  5. 阶段一:AsyncTask的三个属性值和四个步骤

    “阶段一”是指我第一次系统地学习Android开发.这主要是对我的学习过程作个记录. 最近学到用AsyncTask来处理有关网络的操作.虽然代码看上去不是很复杂,但仍有很多地方有疑惑.所以研读了一下A ...

  6. Android必学——AsyncTask

    第一章  AsyncTask的基本构成 为是么要异步任务 1)Android单线程模型 2)耗时操作放在非主线程中执行 AsyncTask为何而生 1)子线程中跟新UI 2)封装.简化异步操作 pub ...

  7. [Android Pro] AsyncTaskLoader vs AsyncTask

    reference to : http://blog.csdn.net/a910626/article/details/45599133 我看了一下asyncTask是从LV3开始,AsyncTask ...

  8. AsyncTask异步上传文本到服务器

    服务器代码:用于接收客户端信息 package ches; import java.io.IOException; import java.io.PrintWriter; import javax.s ...

  9. Android-异步任务-AsyncTask

    什么是异步任务? 异步任务就是开一个子线程,然后让它去跑,它跑完了就会回来告诉你说,它跑完了,这是结果.这和Java中的回调差不多.我们在OKHttp中很长见到的 onSuccess() 和 onEr ...

  10. java 线程池ThreadPoolExecutor 如何与 AsyncTask() 组合使用。

    转载请声明出处谢谢!http://www.cnblogs.com/linguanh/ 这里主要使用Executors中的4种静态创建线程池实例方法中的 newFixedThreadPool()来举例讲 ...

随机推荐

  1. leetcode之Find All Numbers Disappeared in an Array

    问题来源:Find All Numbers Disappeared in an Array 很久没有刷题了,感觉大脑开始迟钝,所以决定重拾刷题的乐趣.一开始不要太难,选一些通过率高的题目做,然后就看到 ...

  2. Linux Shell编程参考大全

    本文记录Linux Shell编程中常用基本知识,方便快速入门以及查询使用. 本文主要分为以下几个部分: 一.Shell中的变量 任何编程语言中,有关变量的定义,作用范围,赋值等都是最最基础的知识. ...

  3. Bootstrap3 栅格系统-列排序

    通过使用 .col-md-push-* 和 .col-md-pull-* 类就可以很容易的改变列(column)的顺序. <div class="row"> <d ...

  4. MVP框架 – Ted Mosby的软件架构

    作者:Hannes Dorfmann 原文链接 : Ted Mosby – Software Architect 文章出自 : Android开发技术前线 译者 : Mr.Simple 我给这篇关于A ...

  5. Linux 高性能服务器编程——Linux服务器程序规范

    问题聚焦:     除了网络通信外,服务器程序通常还必须考虑许多其他细节问题,这些细节问题涉及面逛且零碎,而且基本上是模板式的,所以称之为服务器程序规范.     工欲善其事,必先利其器,这篇主要来探 ...

  6. 4-sum问题

    给定一个整数数组,判断能否从中找出4个数a.b.c.d,使得他们的和为0,如果能,请找出所有满足和为0个4个数对. #define SIZE 10 void judgeAndPut(int* arr, ...

  7. 21 PagerTabStrip-PagerTitleStrip-viewPager

    PagerTabStrip:可以点击跳转到对应viewPager界面 PagerTitleStrip:不可点击 在eclipse开发时如果目标版本为API23那么会有不显示的问题 解决:更换v4包 解 ...

  8. 18 UI美化之level(等级显示显示)

    根据level显示哪张图片 在工程文件的res/drawable/新建level-list 如下 <?xml version="1.0" encoding="utf ...

  9. Android简易实战教程--第十八话《ListView显示,简单的适配器SimpleAdapter》

    本篇介绍Listview的显示,对于listview有许多的适配器,如ArrayAdapter,BaseAdapter,SimpleAdapter等等.本篇先热身一下,介绍最简单的SimpleAdap ...

  10. iOS日历中给一个事件添加多个提醒

    大熊猫猪·侯佩原创或翻译作品.欢迎转载,转载请注明出处. 如果觉得写的不好请多提意见,如果觉得不错请多多支持点赞.谢谢! hopy ;) iOS自带的日历应用中,我们最多只能给一个事件设置2个提醒,但 ...