构建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 //Asynctask加载网络图片
2 //使用ProgressBar 和ProgressDialog提示下载的进度
3
4 public class ImageTest extends Activity {
5 private ImageView imageView;
6 private ProgressBar progressBar;// 方式一:对话框提示进度
7 String URL = "http://p4.so.qhimgs1.com/sdr/1228_768_/t01f7ed810efbfe800a.jpg";
8 ProgressDialog dialog;// 方式二:对话框提示进度
9 MyAsynctask1 task;
10
11 @Override
12 protected void onCreate(Bundle savedInstanceState) {
13 // TODO Auto-generated method stub
14 super.onCreate(savedInstanceState);
15 setContentView(R.layout.image);
16 imageView = (ImageView) findViewById(R.id.image);
17 progressBar = (ProgressBar) findViewById(R.id.bar);
18
19 dialog = new ProgressDialog(this);
20 dialog.setTitle("提示信息:");
21 dialog.setMessage("laoding....");
22 dialog.setProgressStyle(dialog.STYLE_HORIZONTAL);
23
24 MyAsynctask1 task = new MyAsynctask1();
25 task.execute(URL);
26
27 }
28
29 // 不等进度条满就后退再执行,会发现后面执行的进度条迟迟没有响应,
30 // 令AsyncTask的生命周期和Activity或者Fragment的生命周期保持一致就可以了。
31 @Override
32 protected void onPause() {
33 // TODO Auto-generated method stub
34 super.onPause();
35 if (task != null && task.getStatus() == AsyncTask.Status.RUNNING) {
36 task.cancel(true);
37 // cancel()只是将对应的task标记为cancel状态,并不是真正取消线程执行
38 }
39 }
40
41 // params:启动任务时输入参数的类型。
42 // progress:后台任务执行中,返回进度值的类型。
43 // Result:后台执行任务完成后,返回结果的类型。
44
45 // 2. 执行顺序:onPrRreExecute() --> doInBackground() --> onProgressUpdate() -->
46 // onPostExecute()。
47 class MyAsynctask1 extends AsyncTask<String, Integer, Bitmap> {
48 @Override
49 // 1.执行后台耗时操作之前被调用,通常是用户完成一些初始化操作。
50 protected void onPreExecute() {
51 // TODO Auto-generated method stub
52 super.onPreExecute();
53 // 显示进度条
54 progressBar.setVisibility(View.VISIBLE);
55 dialog.show();
56 }
57
58 // 4.当doInBackground()完成后,系统会自动调用此方法,并将doInBackground()返回的值传给该方法,也就是展示处理完成的结果。
59 @Override
60 protected void onPostExecute(Bitmap result) {
61 // TODO Auto-generated method stub/
62 super.onPostExecute(result);
63 progressBar.setVisibility(View.GONE);// 消失进度条
64 dialog.dismiss();
65 imageView.setImageBitmap(result);
66
67 }
68
69 // 2.
70 // doInBackground:这是AsyncTask子类所必须要重写的方法,异步执行后台线程将要完成的任务。我们所有的耗时操作都将在这个方法中进行操作。
71 @Override
72 protected Bitmap doInBackground(String... params) {
73 // TODO Auto-generated method stub
74
75 // 从params中取出参数值,传给url
76 String url = params[0];
77 // 初始化参数
78 Bitmap bitmap = null;
79 URLConnection connection;
80 InputStream inputStream;
81
82 try {
83 connection = new URL(url).openConnection();
84 inputStream = connection.getInputStream();// 获取输入流
85 BufferedInputStream bis = new BufferedInputStream(inputStream);
86 Thread.sleep(5000);// 睡3秒
87 // 通过decodeStream()解析输入流
88 bitmap = BitmapFactory.decodeStream(bis);
89 inputStream.close();
90 bis.close();
91
92 } catch (IOException e) {
93 // TODO Auto-generated catch block
94 e.printStackTrace();
95 } catch (InterruptedException e) {
96 // TODO Auto-generated catch block
97 e.printStackTrace();
98 }
99 // 模拟进度更新
100 for (int i = 0; i < 100; i++) {
101 if (isCancelled()) {
102 break;
103 }
104 publishProgress(i);
105
106 }
107 return bitmap;
108 }
109
110 // 3.在doInBackground()方法中调用publishProgrsss()更新任务的执行进度后,就会触发该方法(必须先调用publishProgrsss()),就可以知道当前耗时操作的完成进度。
111 @Override
112 protected void onProgressUpdate(Integer... values) {
113 // TODO Auto-generated method stub
114 super.onProgressUpdate(values);
115 if (isCancelled()) {
116 return;
117
118 }
119 dialog.setProgress(values[0]);
120 progressBar.setProgress(values[0]);
121 }
122
123 }
124
125 }
反复执行上一节课的异步加载,而且是不等进度条满就后退再执行,会发现后面执行的进度条迟迟没有响应,为什么呢?这并非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 }