Android攻城狮AsyncTask
Posted 张兮兮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android攻城狮AsyncTask相关的知识,希望对你有一定的参考价值。
构建AsyncTack子类的参数 AsyncTask<Params,Progress,Result>是一个抽象类,通常用于被继承,继承AsyncTask需要指定如下三个泛型参数: params:启动任务时输入参数的类型。 progress:后台任务执行中,返回进度值的类型。 Result:后台执行任务完成后,返回结果的类型。 ------------------------ 如何构建AsyncTask子类的回调方法? 一个完整的AsyncTask通常需要指定如下几个方法: 1. doInBackground:这是AsyncTask子类所必须要重写的方法,异步执行后台线程将要完成的任务。我们所有的耗时操作都将在这个方法中进行操作。 2. onPreExecute:执行后台耗时操作之前被调用,通常是用户完成一些初始化操作。 3. onPostExecute:当doInBackground()完成后,系统会自动调用此方法,并将doInBackground()返回的值传给该方法,也就是展示处理完成的结果。 4. onProgressUpdate:在doInBackground()方法中调用publishProgrsss()更新任务的执行进度后,就会触发该方法(必须先调用publishProgrsss()),就可以知道当前耗时操作的完成进度。 ---------------------------------- 额外补充: 1. 注意这里的例子继承的是 AsyncTask<Void,Void,Void>,需要带上三个泛型,定义Void泛型要注意V是大写。。。 2. 执行顺序:onPrRreExecute() --> doInBackground() --> onProgressUpdate() --> onPostExecute()。
AsyncTask<String,Void,Bitmap>三个参数分别为:url类型,进度值类型,返回值类型。 这里的例子暂时不设置进度值,url设置为String类型,又因为我们加载的是一张Bitmap,所以返回的参数类型设置为 Bitmap。 1. doInBackground(String...params)传进来的是一个可变长数组,也就是说,我们可以传进不止一个参数(通过execute()传进来),这些参数依次存在于这个数组中。现在只有一个参数,所以只要写个params[0]取出对应的URL即可。 2. 定义一个Bitmap,也就是我们所要获取的Bitmap。 3. 定义一个访问网络的URLconnection,也就是一个网络连接对象connection。 4. 定义一个InputStream,用于获取数据的输入流。 5. 初始化connection:connection = new URL(url).openConnection();这里需要自行导入jar包:import java.net.URL; 另外需要try-catch包围。 6. 获取输入流:is = connection.getInputStream(); 7. 对输入流进行包装:BufferedInputStream bis = new BufferedInputStream(is); 8. 通过decodeStream()将输入流解析成 Bitmap:bitmap = BitmapFactory.decodeStream(bis); 9. 关闭输入流、返回 bitmap。
1 public class ImageTest extends Activity { 2 private ImageView imageView; 3 private ProgressBar progressBar; 4 String URL = "http://p4.so.qhimgs1.com/sdr/1228_768_/t01f7ed810efbfe800a.jpg"; 5 6 @Override 7 protected void onCreate(Bundle savedInstanceState) { 8 // TODO Auto-generated method stub 9 super.onCreate(savedInstanceState); 10 setContentView(R.layout.image); 11 imageView = (ImageView) findViewById(R.id.image); 12 progressBar = (ProgressBar) findViewById(R.id.bar); 13 14 MyAsynctask1 task = new MyAsynctask1(); 15 task.execute(URL); 16 17 } 18 19 // params:启动任务时输入参数的类型。 20 // progress:后台任务执行中,返回进度值的类型。 21 // Result:后台执行任务完成后,返回结果的类型。 22 class MyAsynctask1 extends AsyncTask<String, Void, Bitmap> { 23 @Override 24 // 异步初始化 25 protected void onPreExecute() { 26 // TODO Auto-generated method stub 27 super.onPreExecute(); 28 // 显示进度条 29 progressBar.setVisibility(View.VISIBLE); 30 } 31 32 @Override 33 protected void onPostExecute(Bitmap result) { 34 // TODO Auto-generated method stub 35 super.onPostExecute(result); 36 progressBar.setVisibility(View.GONE); 37 imageView.setImageBitmap(result); 38 39 } 40 41 @Override 42 protected Bitmap doInBackground(String... params) { 43 // TODO Auto-generated method stub 44 45 // 从params中取出参数值,传给url 46 String url = params[0]; 47 // 初始化参数 48 Bitmap bitmap = null; 49 URLConnection connection; 50 InputStream inputStream; 51 52 try { 53 connection = new URL(url).openConnection(); 54 inputStream = connection.getInputStream();// 获取输入流 55 BufferedInputStream bis = new BufferedInputStream(inputStream); 56 Thread.sleep(3000);// 睡3秒 57 // 通过decodeStream()解析输入流 58 bitmap = BitmapFactory.decodeStream(bis); 59 inputStream.close(); 60 bis.close(); 61 62 } catch (IOException e) { 63 // TODO Auto-generated catch block 64 e.printStackTrace(); 65 } catch (InterruptedException e) { 66 // TODO Auto-generated catch block 67 e.printStackTrace(); 68 } 69 return bitmap; 70 } 71 72 } 73 74 }
反复执行上一节课的异步加载,而且是不等进度条满就后退再执行,会发现后面执行的进度条迟迟没有响应,为什么呢?这并非bug,而是 AsyncTask 所实行的一种机制。AsyncTask的底层是通过线程池去作用的。当一个线程没有完成的时候,后面的线程就无法开始。我们上一节课用了for()循环去执行进度条 的更新操作,必须等到for()循环结束后才会执行下一个Task。
---------
那么,如何去解决这样的问题呢?
很简单,令AsyncTask的生命周期和Activity或者Fragment的生命周期保持一致就可以了。
回到ProgressBar,重写onPause(),在Activity执行onPause()的时候,对AsyncTask进行判断:
如果AsyncTask不为空且处于Running状态,我们就要取消该线程:
protected void onPause() {
super.onPause();
if(mTask!=null && mTask.getStatus()==AsyncTask.Status.RUNNING){
mTask.cancel(true);
}
}
cancle()方法只是将对应的AsyncTask标记为cancel状态,并不是真正地取消线程的执行。
另外,我们在Java中也是没办法直接粗暴地停止一个线程,我们必须要等一个线程执行完毕之后才能继续其他线程的操作。
--------------
那要如何快速停止线程呢?
1. 在onPause()中标记取消状态:mTask.cancel(true);
既然我们已经标记了cancel状态,那么可以在AsyncTask中监测这样的改变,一旦当前状态改为cancelled,我们就要跳出循环,立刻结束当前操作,从而结束整个线程逻辑。
2. 在doInBackground()方法的for()循环内添加isCancelled()对线程的状态进行判断:
if(isCancelled())break;
3. 同理,在onProgressUpdate()方法中也做类似的处理:
if(isCancelled())return;
通过如上操作,我们就能快速停止当前线程,将处理权交给下一个AsyncTask。
1 public class ProgressBarTest extends Activity { 2 3 ProgressBar progressBar; 4 MyAsycnTask2 task; 5 @Override 6 protected void onCreate(Bundle savedInstanceState) { 7 // TODO Auto-generated method stub 8 super.onCreate(savedInstanceState); 9 setContentView(R.layout.progressbar); 10 progressBar = (ProgressBar) findViewById(R.id.progressBar1); 11 12 task = new MyAsycnTask2(); 13 task.execute(); 14 } 15 16 @Override 17 protected void onPause() { 18 // TODO Auto-generated method stub 19 super.onPause(); 20 if (task!=null&&task.getStatus()==AsyncTask.Status.RUNNING) { 21 task.cancel(true);//cancel()只是将对应的task标记为cancel状态,并不是真正取消线程执行 22 } 23 } 24 25 class MyAsycnTask2 extends AsyncTask<Void, Integer, Void> { 26 27 @Override 28 protected void onProgressUpdate(Integer... values) { 29 // TODO Auto-generated method stub 30 // 获取进度更新值 31 super.onProgressUpdate(values); 32 if (isCancelled()) { 33 return; 34 } 35 progressBar.setProgress(values[0]); 36 } 37 38 @Override 39 protected Void doInBackground(Void... params) { 40 // TODO Auto-generated method stub 41 42 // 模拟进度更新 43 for (int i = 0; i < 100; i++) { 44 if (isCancelled()) { 45 break; 46 } 47 publishProgress(i); 48 try { 49 Thread.sleep(300); 50 } catch (InterruptedException e) { 51 // TODO Auto-generated catch block 52 e.printStackTrace(); 53 } 54 } 55 return null; 56 } 57 } 58 }
以上是关于Android攻城狮AsyncTask的主要内容,如果未能解决你的问题,请参考以下文章