AsyncTask的使用
1、AsyncTask可以操作耗时操作,也可以进行UI更新,是Thread与Handler的结合,内部通过线程池来封装,不熟悉异步操作机制(Handler,Message,Looper)的朋友,可以先了解下Handler的异步机制,可以参考鸿洋大神的这篇博客:http://blog.csdn.net/lmj623565791/article/details/38377229,以及郭神的这篇从源码解析HandHandler机制http://blog.csdn.net/guolin_blog/article/details/9991569
2、来,先shan上代码,简单的例子,通过URL获取一张图片并显示
class ImageTask extends AsyncTask<String,Void,Bitmap>{
@Override
protected void onPreExecute() {
super.onPreExecute();
mImageView.setVisibility(View.GONE);
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
protected Bitmap doInBackground(String... strings) {
Log.e("zzz", "ImageTask--doInBackground");
String url = strings[0];
Bitmap bitmap = null;
URLConnection connection = null;
InputStream is = null;
BufferedInputStream bis = null;
try {
Thread.sleep(3000);
connection = new URL(url).openConnection();
is = connection.getInputStream();
bis = new BufferedInputStream(is);
bitmap = BitmapFactory.decodeStream(bis);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
is.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//返回bitmap对象
return bitmap;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
Log.e("zzz", "ImageTask--onPostExecute");
Message message = mHandler2.obtainMessage();
message.sendToTarget();
if(bitmap != null){
mImageView.setImageBitmap(bitmap);
mProgressBar.setVisibility(View.GONE);
mImageView.setVisibility(View.VISIBLE);
}
}
}
3、继承AsyncTask时三个参数
1)、第一个参数是传递给doInBackground方法(参数列表);这里传入的是图片的URL,即String类型
2)、第二个参数是进度条参数,在后台进行任务操作时,需要在当前界面显示进度;这里传入的是Void类型,不需要显示进度
3)、第三个参数是doInBackground执行完以后返回的类型,也就是传递给onPostExecute方法参数,这里是Bitmap,
4、AsyncTask中的方法
1)、doInBackground(Params...)是继承AsyncTask必须实现的方法,该方法是在子线程中执行,可以执行耗时操作,接收第一个参数的泛型,执行的结果返回给onPostExecute方法参数,另外在doInBackground调用publishProgress(第二个参数泛型),可以更新操作进度;
2)、onPreExecute()是在执行耗时操作(doInBackground)之前调用,可以显示UI提示,比如加载对话框;
3)、onPostExecute(Result) 在doInBackground方法执行完返回结果会调用,返回的结果作为数据传递给该方法的参数,可以做一些UI操作,比如关闭对话框,任务操作的结果显示;
4)、onProgressUpdate(Progress..)当在后台任务方法doInBackground方法中diao调用了publishProgress这个方法,则hui 调用该方法,参数是publishProgress中传递过来的数据,可以刷新UI进度显示,
5、在Async中模拟进度显示,代码如下
public class ProgressBarTest extends AppCompatActivity {
private ProgressBar mProgressBar;
private ProgressBarTask mTask;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.progressbar_activty);
mProgressBar = findViewById(R.id.progress_bar);
mTask = new ProgressBarTask();
mTask.execute();
}
@Override
protected void onStop() {
super.onStop();
//将AsyncTask线程标记为取消状态
if(mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING){
//发送一请求,但并不是真正的取消线程,线程仍在执行,相当于一信号量
mTask.cancel(true);
}
}
/**
* 三个参数,第一个参数是doInBackground方法中的参数,
* 第二个参数是进度条中的参数
* 第三个参数是onPostExecute方法中的参数,是由doInBackground方法执行完返回的
*/
class ProgressBarTask extends AsyncTask<Void,Integer,Void>{
@Override
protected Void doInBackground(Void... voids) {
for(int i = 0; i < 100; i++){
if (isCancelled()){
break;
}
//进度传递
publishProgress(i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if(isCancelled()){
return;
}
mProgressBar.setProgress(values[0]);
}
}
}
注意☆☆☆☆☆:会出现一个问题,当进入该界面,模拟进度显示正常,然后退出,再进入,发现进度显示没有刷新,为什么会这样?
mTask.execute();这是前一个线程没有执行完,后一个线程在等待前一个执行后才会执行,其内部是相当于单线程操作,知道原因了,怎么解决?
将AsyncTask与Activity或者Fragment的生命周期绑在一起,调用AsyncTask的quxi取消方法,如在Activity中的onPause()方法这么操作
if(mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING){
//发送一请求,但并不是真正的取消线程,线程仍在执行,相当于一信号量
mTask.cancel(true);
}
重新运行,发现依旧是上述现象,第一次执行进度更新,退出,再进入进度又没有刷新,发现mTask.cancel(true);只是将该线程标记为取消,而没有真正取消,线程依旧还在执行,在AsyncTask中的doInBackground方法进行如下判断
if (isCancelled()){
break;
}
这样线程就会执行中断,执行其他的线程,或者如果觉得这样操作太麻烦,可以改调用方式如下:
// mTask.execute();
mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
这样也有一个问题就是当线程数达到核心线程数时,会进入队列等待,所以推荐使用第一种方法跟Activity或Fragment声明周期绑定的方式取消AsyncTask的任务,也可以使用自定义线程池的方式,
6、AsyncTask遇到的坑,
new ImageTask().execute(URL);
new ProgressBarTask().execute();
同时执行两AsyncTask时,发现必须等第一个AsyncTask的任务执行完以后,才会执行第二个AsyncTask
日志如下图:
E/zzz: ImageTask--doInBackground
E/zzz: ImageTask--onPostExecute
E/zzz: values0
E/zzz: values1
E/zzz: values2
E/zzz: values3
E/zzz: values4
E/zzz: values5
E/zzz: values6
解决办法
//new ProgressBarTask().execute();
new ProgressBarTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
这样就nice,至于为啥会这样,请看下篇AsyncTask源码分析