AsyncTask的使用
来源:互联网 发布:淘宝如何转接人工客服 编辑:程序博客网 时间:2024/05/18 09:06
概念
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; } @Override protected void onPreExecute() { super.onPreExecute(); progressBar.setProgress(0); } @Override protected Bitmap doInBackground(String... params) { //获取路径 String path = params[0]; //获取图片 Bitmap bitmap = downloadUrlBitmap(path); return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { super.onPostExecute(bitmap); img_center.setImageBitmap(bitmap); progressBar.setProgress(0); } @Override protected 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; @Override protected 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(); } @Override protected 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
@Override protected 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可以有多个。
回调方法
onPreExecute():在执行后台下载操作之前调用,运行在主线程中;
doInBackground():核心方法,执行后台下载操作的方法,必须实现的一个方法,运行在子线程中;
onPostExecute():后台下载操作完成后调用,运行在主线程中;因此,AsyncTask的基本生命周期过程为:onPreExecute() –> doInBackground() –> onPostExecute()。
onProgressUpdate():在下载操作doInBackground()中调用publishProgress()时的回调方法,用于更新下载进度,运行在主线程中;
注意的地方
每一个new出的AsyncTask只能执行一次execute()方法,多次运行将会报错,如需多次,需要新new一个AsyncTask;
AsyncTask必须在UI线程中创建实例,execute()方法也必须在UI线程中调用;
AsyncTask为我们提供了cancel()方法来取消一个任务的执行,但是要注意的是,cancel方法并没有能力真正去取消一个任务,其实只是设置这个任务的状态为取消状态,我们需要在doInBackground()下载中进行检测,一旦检测到该任务被用户取消了,立即停止doInBackground()方法的执行。
不要手动的调用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的使用
- AsyncTask类的使用
- Android 的AsyncTask使用
- AsyncTask的基本使用
- AsyncTask的简单使用
- AsyncTask的简单使用
- Android AsyncTask的使用
- AsyncTask的使用
- AsyncTask的简单使用
- Android AsyncTask 的使用
- AsyncTask的使用
- AsyncTask类的使用
- AsyncTask的使用
- AsyncTask的使用步骤
- AsyncTask的使用
- 使用Android的AsyncTask
- Android AsyncTask 的使用
- AsyncTask的使用
- eclipse插件
- php初次使用以及关于require include require_once include_once
- a标签伪类选择器
- node.js post方式的表单提交
- 从尾到头打印链表
- AsyncTask的使用
- linux学习路线
- 10分钟学会Git教程
- SGU
- 队列、堆、栈、堆栈的区别
- Linux常用指令-----ldd
- 替换空格
- 最大熵阈值分割法
- Codewars进阶之路——Getting along with Integer Partitions(DFS)