Android异步任务:AsyncTask 和 Handler+Message详解

首先说下这是一个很枯燥的文章,因为没什么效果;但是个人感觉还是非常有用的;所以今天写一下个人对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;  因为sleepThread的方法,第一个run方法是在Thread方法内,第二个run是内名对象

在子线程中做耗时操作完成后发送message也有两种方式:

第一种就是我们demo中使用的这种;

第二种就是发送空消息;不用创建msg,直接发送,然后handler收到这个空消息做指定操作;

//这个100可以理解为一个标示,因为在handler接收的时候需要来看这个标示判断做什么操作
handler.sendEmptyMessage(100);
  
//在这里面做一个switch判断:因为有可能一个类中发送多个消息执行不同的操作
            switch (msg.what){
                case 100:
                    //做相应的操作
                    break;
            }


好了,基本用法就这些了,下面说下两者的优缺点,以便日后大家用到之时选择用哪个:


AsyncTask:

优点:简单,快捷,过程可控;

缺点:使用多个异步操作并需要修改UI时,就非常复杂了;

 

Handler+Message:

优点:结果清晰,功能定义明确;执行多个后台任务时简单;

缺点:在单个后台异步处理时,显得代码稍多,结构稍复杂;

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值