概念

AsyncTask是一个抽象类,它是由Android封装的一个轻量级异步类,它的内部封装了两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler),其中SerialExecutor线程池用于任务的排队,THREAD_POOL_EXECUTOR线程池才真正地执行任务,InternalHandler用于从工作线程切换到主线程。

为什么要用AsyncTask?

android的ui线程操作并不是安全的,并且和用户直接进行界面交互的操作都必须在ui线程中进行才可以。这种模式叫做单线程模式。

我们写App都有一个原则,主线程不能够运行需要占用大量CPU时间片的任务,如大量复杂的浮点运算,较大的磁盘IO操作,网络socket等,这些都会导致我们的主线程对用户的响应变得迟钝,甚至ANR,这些会使应用的用户体验变差,但是有时又的确需要执行这些耗时的任务,那么我们通常可以使用AsyncTask或者new Thread来处理,这样把任务放入工作线程中执行,不会占用主线程的时间片,所以主线程会及时响应用户的操作,如果使用new Thread来执行任务,那么如果需要中途取消任务执行或者需要返回任务执行结果,就需要我们自己维护很多额外的代码。

一个加载图片的例子:


LoadImageAsyncTask

package com.bourne.android_common.ServiceDemo;import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;
import android.widget.ProgressBar;import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;public class LoadImageAsyncTask extends AsyncTask<String, Integer, Bitmap> {/*** 进度条*/private ProgressBar progressBar;/*** 图片控件*/private ImageView img_center;public LoadImageAsyncTask(ProgressBar pb, ImageView iv) {this.progressBar = pb;this.img_center = iv;}@Overrideprotected void onPreExecute() {super.onPreExecute();progressBar.setProgress(0);}@Overrideprotected Bitmap doInBackground(String... params) {//获取路径String path = params[0];//获取图片Bitmap bitmap = downloadUrlBitmap(path);return bitmap;}@Overrideprotected void onPostExecute(Bitmap bitmap) {super.onPostExecute(bitmap);img_center.setImageBitmap(bitmap);progressBar.setProgress(0);}@Overrideprotected void onProgressUpdate(Integer... values) {super.onProgressUpdate(values);//通过isCancelled()判断任务任务是否被取消if (isCancelled()) {return;}//显示进度progressBar.setProgress(values[0]);}/*** 下载图片** @param urlString* @return*/private Bitmap downloadUrlBitmap(String urlString) {HttpURLConnection urlConnection = null;BufferedInputStream in = null;Bitmap bitmap = null;try {final URL url = new URL(urlString);urlConnection = (HttpURLConnection) url.openConnection();InputStream is = urlConnection.getInputStream();//这里只是为了演示更新进度的功能,实际的进度值需要在从输入流中读取时逐步获取for (int i = 0; i < 100; i++) {if (isCancelled()) {//通过isCancelled()判断任务任务是否被取消break;}publishProgress(i);Thread.sleep(10);//为了看清效果,睡眠一段时间}//实际项目中如何获取文件大小作为进度值及更新进度值
//            int totalSize = urlConnection.getContentLength();//获取文件总大小
//            Logout.e("总长度:" + totalSize);
//            int size = 0;//保存当前下载文件的大小,作为进度值
//            int count = 0;
//            byte[] buffer = new byte[1024];//            while ((count = is.read(buffer)) != -1) {//                size += count;//获取已下载的文件大小
//                //调用publishProgress更新进度,它内部会回调onProgressUpdate()方法
//                float rsult = (float) size / totalSize * 100;
//                DecimalFormat decimalFormat = new DecimalFormat(".00");
//                String rsult2 = decimalFormat.format(rsult);
//                float i = Float.valueOf(rsult2);
//                int i2 = (int) i;
//                publishProgress(i2);
//                Thread.sleep(100);//为了看清效果,睡眠一段时间
//            }in = new BufferedInputStream(is, 8 * 1024);bitmap = BitmapFactory.decodeStream(in);} catch (final IOException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();} finally {if (urlConnection != null) {urlConnection.disconnect();}try {if (in != null) {in.close();}} catch (final IOException e) {e.printStackTrace();}}return bitmap;}
}

这个类的作用是加载一张图片,并在加载过程中更新进度条,最后加载完毕的时候显示图片。

AsyncTaskActivity

package com.bourne.android_common.ServiceDemo;import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;import com.bourne.android_common.R;import java.util.ArrayList;
import java.util.List;import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;public class AsyncTaskActivity extends AppCompatActivity {/*** 图片地址集合*/private String url[] = {"http://img0.imgtn.bdimg.com/it/u=1597254274,1405139366&fm=23&gp=0.jpg","http://img0.imgtn.bdimg.com/it/u=3901634069,2243065451&fm=23&gp=0.jpg","http://img4.imgtn.bdimg.com/it/u=1800624712,2677106110&fm=23&gp=0.jpg","http://img0.imgtn.bdimg.com/it/u=2456066925,446683653&fm=23&gp=0.jpg","http://img0.imgtn.bdimg.com/it/u=565155430,1247415230&fm=23&gp=0.jpg","http://img4.imgtn.bdimg.com/it/u=2845715753,1348257911&fm=23&gp=0.jpg","http://img3.imgtn.bdimg.com/it/u=3634032659,2514353810&fm=23&gp=0.jpg"};private List<LoadImageAsyncTask> tasks = new ArrayList<LoadImageAsyncTask>();private int count = 0;@BindView(R.id.progressBar)ProgressBar progressBar;@BindView(R.id.img_center)ImageView img_center;@BindView(R.id.startLoad)Button startLoad;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_async_task);ButterKnife.bind(this);}@OnClick(R.id.startLoad)public void onClick(View view) {
//        clearTasks();LoadImageAsyncTask loadImageAsyncTask = new LoadImageAsyncTask(progressBar, img_center);//随机读取
//        Random random = new Random();
//        int index = url.length;
//        String path = url[random.nextInt(index)];
//        Logout.e("path:" + path);//按顺序读取int index = url.length;String path = url[count % index];//        //单线程loadImageAsyncTask.execute(path);
//        //单线程
//        loadImageAsyncTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, path);//多线程
//        loadImageAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, path);tasks.add(loadImageAsyncTask);count++;}/*** 清理所有任务*/private void clearTasks() {for (int i = 0; i < tasks.size(); i++) {AsyncTask asyncTask = tasks.get(i);if (asyncTask != null && asyncTask.getStatus() == AsyncTask.Status.RUNNING) {//cancel只是将对应的任务标记为取消状态asyncTask.cancel(true);}}tasks.clear();}@Overrideprotected void onPause() {super.onPause();clearTasks();}}

xml布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context="com.bourne.android_common.ServiceDemo.AsyncTaskActivity"><Button
        android:id="@+id/startLoad"android:layout_width="match_parent"android:textAllCaps="false"android:layout_height="wrap_content"android:text="Start  Load Image"/><ProgressBar
        android:id="@+id/progressBar"style="@style/Widget.AppCompat.ProgressBar.Horizontal"android:layout_width="match_parent"android:layout_height="30dp"android:layout_margin="20dp"android:max="100"android:progress="0"/><ImageView
        android:id="@+id/img_center"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"/>
</LinearLayout>

主要是使用了单线程去加载,所以就算点击多次都会在上一次加载完毕之后才会自动加载下一张,和Handler消息传递机制一样,都是传递多次消息,按顺序执行。

取消任务

AsyncTask为我们提供了cancel()方法来取消一个任务的执行,但是要注意的是,cancel方法并没有能力真正去取消一个任务,其实只是设置这个任务的状态为取消状态,我们需要在doInBackground()下载中进行检测,一旦检测到该任务被用户取消了,立即停止doInBackground()方法的执行。

当我们想每次加载新图片的时候取消之前的任务可以这样做:

    private List<LoadImageAsyncTask> tasks = new ArrayList<LoadImageAsyncTask>();
 public void onClick(View view) {clearTasks();LoadImageAsyncTask loadImageAsyncTask = new LoadImageAsyncTask(progressBar, img_center);//单线程loadImageAsyncTask.execute(path);tasks.add(loadImageAsyncTask);}
    /*** 取消所有任务*/private void clearTasks() {for (int i = 0; i < tasks.size(); i++) {AsyncTask asyncTask = tasks.get(i);if (asyncTask != null && asyncTask.getStatus() == AsyncTask.Status.RUNNING) {//cancel只是将对应的任务标记为取消状态asyncTask.cancel(true);}}tasks.clear();}

AsyncTask

    @Overrideprotected void onProgressUpdate(Integer... values) {super.onProgressUpdate(values);//通过isCancelled()判断任务任务是否被取消if (isCancelled()) {return;}//显示进度progressBar.setProgress(values[0]);}
  /*** 下载图片** @param urlString* @return*/private Bitmap downloadUrlBitmap(String urlString) {HttpURLConnection urlConnection = null;BufferedInputStream in = null;Bitmap bitmap = null;try {final URL url = new URL(urlString);urlConnection = (HttpURLConnection) url.openConnection();InputStream is = urlConnection.getInputStream();//这里只是为了演示更新进度的功能,实际的进度值需要在从输入流中读取时逐步获取for (int i = 0; i < 100; i++) {if (isCancelled()) {//通过isCancelled()判断任务任务是否被取消break;}publishProgress(i);Thread.sleep(10);//为了看清效果,睡眠一段时间}in = new BufferedInputStream(is, 8 * 1024);bitmap = BitmapFactory.decodeStream(in);} catch (final IOException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();} finally {if (urlConnection != null) {urlConnection.disconnect();}try {if (in != null) {in.close();}} catch (final IOException e) {e.printStackTrace();}}return bitmap;}

效果:

多线程

在SDK3.0以前的版本执行asyncTask.execute(task);时的确是多线程并发执行的,线程池大小为5,最大可达128个,google在3.0以后的版本中做了修改,将asyncTask.execute(task);修改为了顺序执行,即只有当一个的实例的任务完成后在执行下一个实例的任务。

那么怎么才能并发执行呢,很简单,3.0后新增了一个方法executeOnExecutor(Executor
exec, Object… params),该方法接受2个参数,第一个是Executor,第二个是任务参数。第一个是线程池实例,google为我们预定义了两种:第一种是AsyncTask.SERIAL_EXECUTOR,第二种是AsyncTask.THREAD_POOL_EXECUTOR,顾名思义,第一种其实就像3.0以后的execute方法,是顺序执行的。第二种就是3.0以前的execute方法,是可以并发执行的。我们直接用asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, task);就可以多任务并发执行了。

 loadImageAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, path);

效果:

可见,当我们多次点击之后,进度条和图片控件都是一个混乱的状态,那是因为多个任务同时改变UI的结果。

三个泛型类型

AsyncTask<Params, Progress, Result>

Params:开始异步任务执行时传入的参数类型,即doInBackground()方法中的参数类型;

Progress:异步任务执行过程中,返回下载进度值的类型,即在doInBackground中调用publishProgress()时传入的参数类型;

Result:异步任务执行完成后,返回的结果类型,即doInBackground()方法的返回值类型;

AsyncTask抽象类,有三个泛型参数类型,第一个是你需要传递进来的参数类型,第二个是任务完成进度的类型一般是Integer,第三个是任务完成结果的返回类型,有时这些参数不是全部需要,不需要的设为Void即可,另外Result只有一个,但Params可以有多个。

回调方法

  1. onPreExecute():在执行后台下载操作之前调用,运行在主线程中;

  2. doInBackground():核心方法,执行后台下载操作的方法,必须实现的一个方法,运行在子线程中;

  3. onPostExecute():后台下载操作完成后调用,运行在主线程中;因此,AsyncTask的基本生命周期过程为:onPreExecute() –> doInBackground() –> onPostExecute()。

  4. onProgressUpdate():在下载操作doInBackground()中调用publishProgress()时的回调方法,用于更新下载进度,运行在主线程中;

注意的地方

  1. 每一个new出的AsyncTask只能执行一次execute()方法,多次运行将会报错,如需多次,需要新new一个AsyncTask;

  2. AsyncTask必须在UI线程中创建实例,execute()方法也必须在UI线程中调用;

  3. AsyncTask为我们提供了cancel()方法来取消一个任务的执行,但是要注意的是,cancel方法并没有能力真正去取消一个任务,其实只是设置这个任务的状态为取消状态,我们需要在doInBackground()下载中进行检测,一旦检测到该任务被用户取消了,立即停止doInBackground()方法的执行。

  4. 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法

AsyncTask和Handler的比较

AsyncTask:

优点:AsyncTask是一个轻量级的异步任务处理类,轻量级体现在,使用方便、代码简洁上,而且整个异步任务的过程可以通过cancel()进行控制;

缺点:不适用于处理长时间的异步任务,一般这个异步任务的过程最好控制在几秒以内,如果是长时间的异步任务就需要考虑多线程的控制问题;当处理多个异步任务时,UI更新变得困难。

Handler:

优点:代码结构清晰,容易处理多个异步任务;

缺点:当有多个异步任务时,由于要配合Thread或Runnable,代码可能会稍显冗余。

总之,AsyncTask不失为一个非常好用的异步任务处理类,只要不是频繁对大量UI进行更新,可以考虑使用;而Handler在处理大量UI更新时可以考虑使用。

推荐大家使用AsyncTask代替Thread+Handler的方式,不仅调用上更为简单,经过实测更可靠一些,Google在Browser中大量 使用了异步任务作为处理耗时的I/O操作,比如下载文件、读写数据库等等,它们在本质上都离不开消息,但是AsyncTask相比Thread加 Handler更为可靠,更易于维护,但AsyncTask缺点也是有的比如一旦线程开启即dobackground方法执行后无法给线程发送消息,仅能 通过预先设置好的标记来控制逻辑,当然可以通过线程的挂起等待标志位的改变来通讯,对于某些应用Thread和Handler以及Looper可能更灵 活。

AsyncTask的全局线程池只有5个工作线程,也就是说,一个APP如果运用AsyncTask技术来执行线程,那么同一时间最多只能有5个线程同时运行,其他线程将被阻塞(注:不运用AsyncTask执行的线程,也就是自己new出来的线程不受此限制),所以AsyncTask不要用于多线程取网络数据,因为很可能这样会产生阻塞,从而降低效率。

能否同时并发100+asynctask呢?

AsyncTask用的是线程池机制,容量是128,最多同时运行5个core线程,剩下的排队。

参考链接

  • Android 异步任务 AsyncTask 的使用与原理分析

  • Android AsyncTask实现原理和使用技巧分享

  • Android Asynctask与Handler的比较,优缺点区别,Asynctask源码

  • Android线程—AsyncTask的使用及原理

AsyncTask的使用相关推荐

  1. android asynctask源码分析,Android通过Handler与AsyncTask两种方式动态更新ListView(附源码)...

    本文实例讲述了Android通过Handler与AsyncTask两种方式动态更新ListView的方法.分享给大家供大家参考,具体如下: 有时候我们需要修改已经生成的列表,添加或者修改数据,noti ...

  2. 【Android】AsyncTask异步类

    一.关于AysncTask AsyncTask使得多线程编程更加简单,AsyncTask能在后台线程执行异步任务,并且在UI线程更新界面,而不再需要用户去操作Thread和Handler.AysncT ...

  3. Asynctask源码分析

    ​ 首先我们使用AsyncTask时,一般是: new AsyncTask(...).execute() 复制代码 我们看new AsyncTask(),它走的是: public AsyncTask( ...

  4. Android 多线程-----AsyncTask详解

    您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内 ...

  5. android一个简单的异步AsyncTask下载数示例,简单下载(07)

    2019独角兽企业重金招聘Python工程师标准>>> public class MainActivity extends Activity {private ImageView i ...

  6. Android 消息异步处理之AsyncTask

    Android提供了异步处理消息的方式大致有两种,第一种是handler+Thread,之前已经对于这种方式做过分析,第二种就是AsyncTask,这是Android1.5提供的一种轻量级的工具类,其 ...

  7. Android 学习笔记--android——AsyncTask在Android4.X的机制问题

    AsyncTask在Android4.X的机制问题 AsyncTask在Android4.X机制是串行的...单一AsyncTask运行完才会运行下一个 AsyncTask在Android2.3机制是 ...

  8. AsyncTask进度条加载网站数据到ListView

    2019独角兽企业重金招聘Python工程师标准>>> 代码介绍: 初学android,写了个一小demo.功能很简单,主要是用来学习, 知识要点: 1.android全局变量的使用 ...

  9. 源码篇——AsyncTask机制

    AsyncTask new AsyncTask<String,String,String>(){// 运行在主线程中,做预备工作onPreExecute(){}// 运行在子线程中,做耗时 ...

  10. Android中 AsyncTask

    Android AsyncTask 在程序处理中必然会遇上耗时的操作,如访问网络,下载数据,访问数据库等,如何存在耗时的操作 又不能影响界面显示交互. 在某些耗时可以控制的情况下,我们可以分批操作,对 ...

最新文章

  1. 字符串-最后一个单词的长度(双指针)
  2. SCCM 2012 R2实战系列之三:独立主站点部署
  3. kettle 表输入 显示重复_表输入插件详解
  4. boost::pointee用法的测试程序
  5. mysql中事务的特性_mysql中事务的四大特性
  6. 广州交警发布路考秘籍 科目三扣分点近80项
  7. truncate,delete,drop之间的区别
  8. 几组图片轮回html,HTML 5 Canvas
  9. 2048的核心算法的初步实现
  10. 等价划分测试c语言测试三角形,三角形等价划分法测试用例
  11. 计算机地图制图 知识总结
  12. 图像isp 详解_【转】 ISP概述、工作原理及架构
  13. 实现spring+mybatis+uncode dal,应用自动切换连接数据库
  14. 阿里云对象存储OSS上传照片(附源码)
  15. 从零构建 React 开发环境(一) —— hello world,麻雀虽小五脏俱全~
  16. 文件夹访问被拒绝,您需要权限来执行操作
  17. 西安理工大学计算机科学与工程学院官网,罗靖-西安理工大学计算机科学与工程学院...
  18. 使用容联云通信实现发送验证码
  19. 国标28181:什么是RTP协议与RTCP协议
  20. 学物理竞赛有多难?应该怎么学?都考什么?看完这篇文章你就懂了!

热门文章

  1. C#使用ManagementObjectSearcher获取本计算机CPU,硬盘,内存条等相关设备信息
  2. BODAS说明书的中文版与相关资料总结
  3. 计算机电子网络战,网络战与电子战融合,''你中有我,我中有你''
  4. 浙江大学计算机与软件学院2019年保研上机模拟练习 --- 凌宸1642
  5. 嵌入式开发--STM32H750VBT6开发中,新版本CubeMX的时钟问题,不能设置到最高速度480MHZ
  6. 关于计算机经历兼职的英文作文,关于兼职的英文作文
  7. python 中m op n运算_m的n次幂python-女性时尚流行美容健康娱乐mv-ida网
  8. Python基于opencv “三维”旋转图片,解决日常小问题
  9. 全国计算机一级考试模拟试题access,2013年全国计算机二级考试Access上机模拟试卷一...
  10. linux写一个最简单的操作系统,自己动手编写一个简单的操作系统