AsyncTask详解
为什么要异步任务
Android为了解决UI线程的阻塞引入了Handler机制和AsyncTask
- Android是单线程模型。在android中只有主线程能够对UI进行操作,而其他线程不能对UI进行操作。这样就保证了UI的稳定性和准确性,避免多个线程同时操作,造成UI混乱。
- 耗时操作放在非主线程中执行。比如网络操作或读取文件,如果放在主线程中执行,那么就会造成后面任务的阻塞,就用可能产生ANR,影响用户体验。
Asynctask
- 线程中更新UI。
- 封装和简化了异步操作。只是操作上简化,效率上并没有。
AsyncTask是抽象类,它定义了三种泛型类型 Params,Progress和Result
- Params 启动任务执行的输入参数,比如HTTP请求的URL。
- Progress 后台任务执行的百分比。
- Result 后台执行任务最终返回的结果类型,比如String。
使用AsyncTask主要有三步
- 创建AsyncTask的子类,并为三个泛型参数赋值。根据需要,可以设置为Void。
- 根据需要实现如下方法:
- onPreExecute(): 该方法在执行后台耗时操作前被主线程(UI线程)调用。常用来初始化工作,如显示进度条等。
- doInBackground(Params…): 在onPreExecute() 方法执行后马上执行,完成耗时操作,在异步线程中执行,不能操作UI。可以调用 publishProgress方法来更新实时的任务进度。抽象方法,必须实现。
- onProgressUpdate(Progress…): 在doInBackground(Params…)调用publishProgress方法后触发该方法,被主线程调用在界面上展示任务的进展情况,例如通过一个进度条进行展示。
- onPostExecute(Result):在doInBackground(Params…)完成后,得到doInBackground(Params…)的返回值,用来操作UI界面。
- 调用AsyncTask子类实例的execute(Params…)来开启异步任务。
一个网络加载图片的例子
创建一个image.xml,简单布局:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<ImageView
android:id="@+id/imageView"
android:layout_height="match_parent"
android:layout_width="match_parent"
/>
<ProgressBar
android:id="@+id/progressBar"
android:visibility="gone"
android:layout_centerInParent="true"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
/>
</RelativeLayout>
ImageDownloadTest.java
package com.qbc.asynctaskdemo;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
import java.net.URL;
public class ImageDownloadTest extends Activity {
private ImageView myImg;
private ProgressBar progressBar;
private static String URL ="http://image.cnwest.com/attachement" +
"/jpg/site1/20110303/001372d899ef0ed99b635c.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image);
myImg= (ImageView) findViewById(R.id.imageView);
progressBar= (ProgressBar) findViewById(R.id.progressBar);
MyAsyncTask myAsyncTask=new MyAsyncTask();
myAsyncTask.execute(URL);//启动AsyncTask,并把参数传递给doInBackground()
}
//创建AsyncTask类
//三个参数分别是URL、进度值和返回值,返回一张图片
class MyAsyncTask extends AsyncTask<String,Void,Bitmap>{
@Override//这个方法在执行耗时操作前被调用。常用来初始化准备工作
protected void onPreExecute() {
super.onPreExecute();
progressBar.setVisibility(View.VISIBLE);//将布局中隐藏的progressbar显示出来
}
@Override//
protected Bitmap doInBackground(String... params) {//这个方法开启异步线程执行耗时操作
String url=params[0];//获取参数
Bitmap bitmap=null;
URLConnection conn;
InputStream inputStream;
try {
conn=new URL(url).openConnection();
inputStream=conn.getInputStream();
BufferedInputStream bis =new BufferedInputStream(inputStream);
//网络太快看不出效果,让它睡一下,真机调试可以去掉。
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bitmap= BitmapFactory.decodeStream(bis);
inputStream.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;//返回一张图片给onPostExecute(Bitmap bitmap)
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
progressBar.setVisibility(View.GONE);//任务完成,隐藏进度条
myImg.setImageBitmap(bitmap);//显示图片
}
}
}
模拟进度条
progressbar.xml很简单。
public class ProgressBarDemo extends Activity {
private ProgressBar mProgressBar;
private ProgressBarAsyncTask mTask=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.progressbar);
mProgressBar= (ProgressBar) findViewById(R.id.progressBar);
mTask=new ProgressBarAsyncTask();
mTask.execute();
}
@Override
protected void onPause() {
super.onPause();
//cancel()只是将对应的AsyncTask标记为cancel状态,并没有真正的取消AsyncTask任务的执行。
//还需要去监测它的改变,然后执行相应操作
if(mTask!=null && mTask.getStatus()==AsyncTask.Status.RUNNING){
mTask.cancel(true);
}
}
class ProgressBarAsyncTask extends AsyncTask<Void,Integer,Void>{
@Override
protected Void doInBackground(Void... params) {
for(int i=0;i<100;i++){
if(mTask.isCancelled()){//监听task是否为cancel状态
break;
}
publishProgress(i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
mProgressBar.setProgress(values[0]);
}
}
}
在onPause()中调用mTask.cancel(true),将对应的AsyncTask标记为cancel状态,然后在doInBackground中调用mTask.isCancelled()进行监听task状态,为cancel状态就break,结束任务,这样就可以使得AsyncTask和Activity的生命周期一样长,反复退出重进的时候就不会等待上一个任务的结束,而是重新开始任务。可以去掉onPause()方法进行测试对比。