一、总述
在Android当中,提供了异步消息处理机制的两种方式来解决线程之间的通信问题,一种是通过Handler的机制(这种方式在后面的博客中将详细介绍),还有一种就是今天要详细讲解的 AsyncTask 机制。
Android中的工作者线程主要有AsyncTask、IntentService、HandlerThread,它们本质上都是Handler与线程池的封装。关于线程和线程池相关知识的介绍,请参考这两篇博文:Java核心技术点之多线程 深入理解Java之线程池
使用AsyncTask的方便之处在于能够更新用户界面,当然这里更新用户界面的操作还是在主线程中完成的,但是由于AsyncTask内部包含一个Handler,所以可以发送消息给主线程让它更新UI。另外,AsyncTask内还包含了一个线程池。使用线程池的主要原因是避免不必要的创建及销毁线程的开销。
二、掌握AsyncTask
我们就必须要一个概念,总结起来就是: 3个泛型,4个步骤。
1、3个泛型参数
我们来看看AsyncTask这个抽象类的定义,当我们定义一个类来继承AsyncTask这个类的时候,需要为其指定3个泛型参数:AsyncTask <Params, Progress, Result>
- Params: 指定的是我们传递给异步任务执行时的参数的类型
- Progress: 指定的是我们的异步任务在执行的时候将执行的进度返回给UI线程的参数的类型
- Result: 指定的是异步任务执行完后返回给UI线程的结果的类型
我们在定义一个类继承AsyncTask类的时候,必须指定好这三个泛型的类型,如果都不指定的话,则都将其写成Void,例如:AsyncTask <Void, Void, Void>
2、4个步骤
当我们执行一个异步任务时,需要按照下面的4个步骤分别执行:
- onPreExecute(): 这个方法是在执行异步任务之前的时候执行,并且是在UI Thread当中执行的,通常我们在这个方法里做一些UI控件的初始化的操作,例如弹出ProgressDialog
- doInBackground(Params... params): 在onPreExecute()方法执行完后,会马上执行这个方法,这个方法就是来处理异步任务的方法,Android操作系统会在后台的线程池当中开启一个worker thread来执行这个方法(即在worker thread当中执行),执行完后将执行结果发送给最后一个 onPostExecute 方法,在这个方法里,我们可以从网络当中获取数据等一些耗时的操作
- onProgressUpdate(Progess... values): 这个方法也是在UI Thread当中执行的,在异步任务执行的时候,有时需要将执行的进度返回给UI界面,例如下载一张网络图片,我们需要时刻显示其下载的进度,就可以使用这个方法来更新进度。这个方法在调用之前,我们需要在 doInBackground 方法中调用一个 publishProgress(Progress) 的方法来将进度时时刻刻传递给 onProgressUpdate 方法来更新
- onPostExecute(Result... result): 当异步任务执行完之后,就会将结果返回给这个方法,这个方法也是在UI Thread当中调用的,我们可以将返回的结果显示在UI控件上
为什么AsyncTask抽象类只有一个 doInBackground 的抽象方法呢??原因是,我们如果要做一个异步任务,我们必须要为其开辟一个新的Thread,让其完成一些操作,而在完成这个异步任务时,我可能并不需要弹出ProgressDialog,并不需要随时更新ProgressDialog的进度条,也并不需要将结果更新给UI界面,所以除了 doInBackground 方法之外的三个方法,都不是必须有的,因此必须要实现的方法是 doInBackground 方法。
4个步骤简洁版描述如下:
第一步:表示任务执行前的操作
第二步:主要完成耗时操作
第三步:主要是更新UI操作
第四步:产生最终结果
以下实例中代表的含义为:
第一步:显示进度条
第二步:(此任务必不可少)在后台执行任务,将进度值传给第三步,将结果传给第四步;
第三步:进度值更新
第四步:产生最终结果
3、4条准则
为了正确的使用AsyncTask类,必须遵守以下几条准则(局限性):
- AsyncTask的实例必须在UI线程中创建
- execute方法必须在UI线程中调用
- 不要手动去调用onPreExecute() doInBaground() onProgressUpdate() onPostExecute()
- AsyncTask的实例只能被执行一次 多次调用的时候将会出现异常
三、实例
public class MyAsyncTask extends AsyncTask<String , Void , Bitmap>{
//上下文对象
private Context context;
private ProgressDialog dialog;
private ImageView imageView;
//构造
public MyAsyncTask(Context context , ImageView imageView){
super();
this.context = context;
this.imageView = imageView;
}
/**
* 实现四个系统回调的方法
*/
/**
* 1.onPreExecute()方法:在后台执行前的准备工作,主线程调用
* 准备工作:做什么?>创建进度对话框
*/
@Override
protected void onPreExecute(){
super.onPreExecute();
dialog = new ProgressDialog();
dialog.setIcon(R.drawable.xxx);
dialog.setTitle("友情提示!");
dialog.setMessage("正在玩命为您加载中");
//下面show()方法不要忘了,否则不显示进度对话框
dialog.show();
}
/**
* 2.doInBackground()方法:非主线程调用,后台的主要工作线程,
* 如耗时的操作(网络下载图片,下载JSON等)都放在这里执行。
* 它是子线程,不可以在这里做UI操作(重要)
* 参数是String类型字符数组,通常为传入的url
*/
@Override
public void doInBackground(String...params){
//网络请求:
/*创建HttpClient的实例*/
HttpClient httpClient = new DefaultHttpClient();
/*创建连接方法的实例,HttpGet()的构造中传入url地址*/
HttpGet httpGet = new HttpGet(params[0]);
try{
/*调用创建好的HttpClient的实例的execute方法来发送创建好的HttpGet或HttpPost请求,并返回HttpResponse对象*/
HttpResponse httpResponse = httpClient.execute(httpGet);
if(httpResponse.getStatusLine().getStatusCode() == 200){
/*返回实体对象*/
HttpEntity entity = httpResponse.getEntity();
byte [] data = EntityUtils.toByteArray(entity);
Bitmap bitmap = BitmapFactory.decodeByteArray(data,0,data.length);
return bitmap;
}
}
catch(Exception e){
e.printStackTrace();
}
finally{
httpClient.getConnectionManager().shutdown();
}
}
/**
* 更新显示进度,主线程调用
*/
@Override
public void onProgressUpdate(Void...values){
super.onProgressUpdate(values);
}
/**
* 在doInBackground执行完成之后调用,后台的计算结果将通过该方法传递到UI线程,回到主线程(主线程调用的)
* 可以实现主线程和子线程之间的数据交互
*/
public void onPostExcute(Bitmap bitmap){
super.onPostExecute(bitmap);
if(bitmap != null){
imageView.setImageBitmap(bitmap);
}
else{
Toast.makeText(context,"下载图片失败",Toast.LENGTH.LONG).show();
}
//关闭进度对话框
dialog.dismiss();
}
}
public class MainActivity extends Activity{
private ImageView imageView;
private MyAsyncTask myAsyncTask;
private String url = "";http://p5.qhimg.com/dmt/490_350_/t01405cf23f986e5ef6.jpg
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
imageView = (ImageView)findViewById(R.id.iv_imageview);
//创建AsyncTask的实例
myAsyncTask = new MyAsyncTask(this,imageView);
}
//点击按钮,开始异步下载图片
public void downloadPic(View view){
/*AsyncTask的实例只能被执行一次*/
myAsyncTask.execute(url);
}
}