为什么要使用异步任务?
我们知道,Android
中只有UI线程(主线程)才能进行对UI的更新操作,其他线程是不能直接操作UI.
这样的好处是保证了UI的稳定性和准确性,避免多个线程同时对UI进行操作而造成UI的混乱.
比如网络操作,文件读取等耗时操作,如果全部放到主线程去执行,可能会造成后面任务的阻塞.
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:
优点:结果清晰,功能定义明确;执行多个后台任务时简单;
缺点:在单个后台异步处理时,显得代码稍多,结构稍复杂;