首先说下这是一个很枯燥的文章,因为没什么效果;但是个人感觉还是非常有用的;所以今天写一下个人对android异步任务的理解;
为什么要使用异步任务?
我们知道,Android中只有UI线程(主线程)才能进行对UI的更新操作,其他线程是不能直接操作UI.
这样的好处是保证了UI的稳定性和准确性,避免多个线程同时对UI进行操作而造成UI的混乱.
但Android是一个多线程的操作系统,我们总不能把所有的任务都放在主线程中进行实现,
比如网络操作,文件读取等耗时操作,如果全部放到主线程去执行,可能会造成后面任务的阻塞.
Android会去检测这种阻塞,当阻塞时间太长的时候,就会抛出ANR异常.
我们需要将这些耗时操作放在非主线程中去执行.这样既避免了Android的单线程模型,又避免了ANR.这时候我们就需要异步操作了;
异步操作android提供了两种:Handler+message 和 AsyncTask
我们先来看一个使用两种方式实现的一个小demo(点击button按钮跳转到一个activity,然后联网获取网络图片,加载到imageview上,联网是耗时操作,所以必须开启异步任务)
可以看到使用两种方式实现起来效果一样,两者的概述啊什么的就不聊了,直接聊demo,最后聊下两者的优缺点,什么时候用哪个;
首先看下AsyncTask实现:
首先我们看下AsyncTask和他的几个方法:
/**
* AsyncTask本身是一个抽象类,后面的三个泛型是必须要写的:
*
* Params:启动任务时输入的参数类型
* Progress:后台任务执行中返回进度值的类型.
* Result:后台任务执行完成后返回结果的类型
*
*/
public class MyAsyncTask extends AsyncTask<Void,Void,Void>{
@Override
/**
* 执行耗时操作前调用;
*/
protected void onPreExecute() {
super.onPreExecute();
}
@Override
/**
* 这个方法是必须重写的方法,其他方法随意,用到就写,用不到不用管
* 这个方法就是做耗时操作的,相当于new Thread()方法,所有的耗时操作全部在这做
*
*/
protected Void doInBackground(Void... params) {
return null;
}
@Override
/**
* 当doInBackground()方法完成后执行此方法;
* 并将doInBackground方法返回的值传入此方法.通过此方法进行UI的更新.
*/
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
}
@Override
/**
* 当在doInBackground方法中调用publishProgress方法更新任务执行进度后,将调用此方法.
* 通过此方法我们可以知道任务的完成进度.
*/
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
}
上面这个类和demo无关,只是让不了解的朋友们简单熟悉下AsyncTask;
MainActivity的代码就不复制了,很简单,布局只有一个button,代码只有一个跳转,当点击button的时候跳转到第二个页面;
第二个页面(显示图片的activity),布局是一个相对布局,里面放一个imageview,上面放了一个进度条,当加载图片的时候显示出来,加载完后隐藏掉;
我们来看下第二个activity的代码:
public class ImageActivity extends Activity{
private ImageView mImageView;
private ProgressBar mProgressBar;
String url="http://image.tianjimedia.com/uploadImages/2015/129/56/J63MI042Z4P8.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image);
init();
}
private void init() {
mImageView= (ImageView) findViewById(R.id.imageview);
mProgressBar= (ProgressBar) findViewById(R.id.progressbar);
ImageAsyncTask myAsyncTast = new ImageAsyncTask(mImageView,mProgressBar);
/**
* 启动异步任务的处理
* 只能在UI线程中调用AsyncTask的execute方法.
* 每个AsyncTask只能被执行(execute方法)一次,多次执行将会引发异常
*
*/
myAsyncTast.execute(url);
}
}
自定义AsyncTask的代码:
/**
* 执行顺序:onPreExecute-->doInBackground-->onPostExecute
* 三个参数的的解释:第一个是指 doInBackground中接收的参数的类型
* 第二个是指onProgressUpdate中接收的参数的类型
* 第三个是每日doInBackground返回值的类型以及onPostExecute接收的参数的类型
*
* task.execute(url); 图片的url就是传到doInBackground中的参数
*/
public class ImageAsyncTask extends AsyncTask<String, Void, Bitmap> {
private ImageView mImageView;
private ProgressBar mProgressBar;
public ImageAsyncTask(ImageView mImageView, ProgressBar mProgressBar) {
this.mImageView = mImageView;
this.mProgressBar = mProgressBar;
}
@Override
//用于异步处理前的操作
protected void onPreExecute() {
super.onPreExecute();
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
//所有的异步任务都在这进行;这个params是一个数组,可以传递过来多个值,我们这里只用到一个
protected Bitmap doInBackground(String... params) {
//获取传进来的参数
String url = params[0];
Bitmap bitmap = null;
URLConnection connection;
InputStream is;
try {
connection = new URL(url).openConnection();
is = connection.getInputStream();
//为了更清楚的看到加载图片的等待操作,将线程休眠3秒钟.
Thread.sleep(1000);
BufferedInputStream bis = new BufferedInputStream(is);
//通过decodeStream方法解析输入流
bitmap = BitmapFactory.decodeStream(bis);
is.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return bitmap;
}
@Override
//用于UI的更新.此方法的参数为doInBackground方法返回的值
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
//隐藏进度条
mProgressBar.setVisibility(View.GONE);
//将图片设置到view
mImageView.setImageBitmap(bitmap);
}
}
这里面为了演示异步任务,所以使用IO流来;在真实的项目中可以直接使用glide;
下面我们看下Handler+Message怎么实现:
public class ImageActivity extends Activity{
private ImageView mImageView;
private ProgressBar mProgressBar;
String url="http://image.tianjimedia.com/uploadImages/2015/129/56/J63MI042Z4P8.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image);
init();
}
private void init() {
mImageView= (ImageView) findViewById(R.id.imageview);
mProgressBar= (ProgressBar) findViewById(R.id.progressbar);
//这时候需要加载网络图片,属于耗时操作,必须放到子线程中执行
<span style="color:#ff6666;">new Thread(){
</span> @Override
public void run() {
mProgressBar.setVisibility(View.VISIBLE);
Bitmap bitmap = null;
URLConnection connection ;
InputStream is ;
try {
connection = new URL(url).openConnection();
is = connection.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
//通过decodeStream方法解析输入流
bitmap = BitmapFactory.decodeStream(bis);
is.close();
bis.close();
<span style="color:#ff6666;">//创建message
Message msg = Message.obtain();
//给msg赋值,可以赋任何类型的值
msg.obj=bitmap;
//延时一秒发送消息,一旦handler收到消息立即执行
handler.sendMessageDelayed(msg,1000);
</span> } catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
<span style="color:#ff6666;"> private Handler handler=new Handler(){
@Override
//只要子线程一发消息,此方法马上执行,并且是在主线程中做的,更新UI的操作可以在这里面做
public void handleMessage(Message msg) {
mProgressBar.setVisibility(View.GONE);
//获取图片
Bitmap bitmap= (Bitmap) msg.obj;
mImageView.setImageBitmap(bitmap);
}
</span> };
}
开启子线程也有两种方法开启:
new Thread(){
public void run() {
};
}.start();
new Thread(new Runnable() {
public void run() {
}
}).start();
第一个方法可以直接sleep,第二种方法Thread.sleep; 因为sleep是Thread的方法,第一个run方法是在Thread方法内,第二个run是内名对象
在子线程中做耗时操作完成后发送message也有两种方式:
第一种就是我们demo中使用的这种;
第二种就是发送空消息;不用创建msg,直接发送,然后handler收到这个空消息做指定操作;
//这个100可以理解为一个标示,因为在handler接收的时候需要来看这个标示判断做什么操作
handler.sendEmptyMessage(100);
//在这里面做一个switch判断:因为有可能一个类中发送多个消息执行不同的操作
switch (msg.what){
case 100:
//做相应的操作
break;
}
好了,基本用法就这些了,下面说下两者的优缺点,以便日后大家用到之时选择用哪个:
AsyncTask:
优点:简单,快捷,过程可控;
缺点:使用多个异步操作并需要修改UI时,就非常复杂了;
Handler+Message:
优点:结果清晰,功能定义明确;执行多个后台任务时简单;
缺点:在单个后台异步处理时,显得代码稍多,结构稍复杂;