Android多线程-----异步(AsyncTask)

一、总述

在Android当中,提供了异步消息处理机制的两种方式来解决线程之间的通信问题,一种是通过Handler的机制(这种方式在后面的博客中将详细介绍),还有一种就是今天要详细讲解的 AsyncTask 机制。

Android中的工作者线程主要有AsyncTask、IntentService、HandlerThread,它们本质上都是Handler与线程池的封装。关于线程和线程池相关知识的介绍,请参考这两篇博文:Java核心技术点之多线程    深入理解Java之线程池

使用AsyncTask的方便之处在于能够更新用户界面,当然这里更新用户界面的操作还是在主线程中完成的,但是由于AsyncTask内部包含一个Handler,所以可以发送消息给主线程让它更新UI。另外,AsyncTask内还包含了一个线程池。使用线程池的主要原因是避免不必要的创建及销毁线程的开销。

二、掌握AsyncTask

我们就必须要一个概念,总结起来就是: 3个泛型,4个步骤。

1、3个泛型参数

我们来看看AsyncTask这个抽象类的定义,当我们定义一个类来继承AsyncTask这个类的时候,需要为其指定3个泛型参数:AsyncTask <Params, Progress, Result>

  • Params: 指定的是我们传递给异步任务执行时的参数的类型
  • Progress: 指定的是我们的异步任务在执行的时候将执行的进度返回给UI线程的参数的类型
  • Result: 指定的是异步任务执行完后返回给UI线程的结果的类型

 我们在定义一个类继承AsyncTask类的时候,必须指定好这三个泛型的类型,如果都不指定的话,则都将其写成Void,例如:AsyncTask <Void, Void, Void>


2、4个步骤

当我们执行一个异步任务时,需要按照下面的4个步骤分别执行:

  • onPreExecute(): 这个方法是在执行异步任务之前的时候执行,并且是在UI Thread当中执行的,通常我们在这个方法里做一些UI控件的初始化的操作,例如弹出ProgressDialog
  • doInBackground(Params... params): 在onPreExecute()方法执行完后,会马上执行这个方法,这个方法就是来处理异步任务的方法,Android操作系统会在后台的线程池当中开启一个worker thread来执行这个方法(即在worker thread当中执行),执行完后将执行结果发送给最后一个 onPostExecute 方法,在这个方法里,我们可以从网络当中获取数据等一些耗时的操作
  • onProgressUpdate(Progess... values): 这个方法也是在UI Thread当中执行的,在异步任务执行的时候,有时需要将执行的进度返回给UI界面,例如下载一张网络图片,我们需要时刻显示其下载的进度,就可以使用这个方法来更新进度。这个方法在调用之前,我们需要在 doInBackground 方法中调用一个 publishProgress(Progress) 的方法来将进度时时刻刻传递给 onProgressUpdate 方法来更新
  • onPostExecute(Result... result): 当异步任务执行完之后,就会将结果返回给这个方法,这个方法也是在UI Thread当中调用的,我们可以将返回的结果显示在UI控件上

 为什么AsyncTask抽象类只有一个 doInBackground 的抽象方法呢??原因是,我们如果要做一个异步任务,我们必须要为其开辟一个新的Thread,让其完成一些操作,而在完成这个异步任务时,我可能并不需要弹出ProgressDialog,并不需要随时更新ProgressDialog的进度条,也并不需要将结果更新给UI界面,所以除了 doInBackground 方法之外的三个方法,都不是必须有的,因此必须要实现的方法是 doInBackground 方法。

4个步骤简洁版描述如下:
  第一步:表示任务执行前的操作
  第二步:主要完成耗时操作
  第三步:主要是更新UI操作
  第四步:产生最终结果
以下实例中代表的含义为:
  第一步:显示进度条
  第二步:(此任务必不可少)在后台执行任务,将进度值传给第三步,将结果传给第四步;
  第三步:进度值更新
  第四步:产生最终结果

3、4条准则

为了正确的使用AsyncTask类,必须遵守以下几条准则(局限性):

  • AsyncTask的实例必须在UI线程中创建
  • execute方法必须在UI线程中调用
  • 不要手动去调用onPreExecute() doInBaground() onProgressUpdate() onPostExecute()
  • AsyncTask的实例只能被执行一次 多次调用的时候将会出现异常

三、实例

public class MyAsyncTask extends AsyncTask<String , Void , Bitmap>{
    //上下文对象
    private Context context;
    private ProgressDialog dialog;
    private ImageView imageView;
    
    //构造
    public MyAsyncTask(Context context , ImageView imageView){
        super();
        this.context = context;
        this.imageView = imageView;
    }
    
    /**
     * 实现四个系统回调的方法
     */
     
    /**
     * 1.onPreExecute()方法:在后台执行前的准备工作,主线程调用
     * 准备工作:做什么?>创建进度对话框
     */
    @Override
    protected void onPreExecute(){
        super.onPreExecute();
        dialog = new ProgressDialog();
        dialog.setIcon(R.drawable.xxx);
        dialog.setTitle("友情提示!");
        dialog.setMessage("正在玩命为您加载中");
        //下面show()方法不要忘了,否则不显示进度对话框
        dialog.show();
    }
    
    /**
     *  2.doInBackground()方法:非主线程调用,后台的主要工作线程,
     *  如耗时的操作(网络下载图片,下载JSON等)都放在这里执行。
     *  它是子线程,不可以在这里做UI操作(重要)
     *  参数是String类型字符数组,通常为传入的url
     */
    @Override
    public void doInBackground(String...params){
        //网络请求:
        /*创建HttpClient的实例*/
        HttpClient httpClient = new DefaultHttpClient();
        /*创建连接方法的实例,HttpGet()的构造中传入url地址*/
        HttpGet httpGet = new HttpGet(params[0]);
        try{
            /*调用创建好的HttpClient的实例的execute方法来发送创建好的HttpGet或HttpPost请求,并返回HttpResponse对象*/
            HttpResponse httpResponse = httpClient.execute(httpGet);
            if(httpResponse.getStatusLine().getStatusCode() == 200){
                /*返回实体对象*/
                HttpEntity entity = httpResponse.getEntity();
                byte [] data = EntityUtils.toByteArray(entity);
                Bitmap bitmap = BitmapFactory.decodeByteArray(data,0,data.length);
                return bitmap;
            }
        }
        catch(Exception e){
            e.printStackTrace();
        }
        finally{
            httpClient.getConnectionManager().shutdown();
        }
    }
    
    /**
     *  更新显示进度,主线程调用
     */
    @Override
    public void onProgressUpdate(Void...values){
        super.onProgressUpdate(values);
    }
    
    /**
     *  在doInBackground执行完成之后调用,后台的计算结果将通过该方法传递到UI线程,回到主线程(主线程调用的)
     *  可以实现主线程和子线程之间的数据交互
     */
    public void onPostExcute(Bitmap bitmap){
        super.onPostExecute(bitmap);
        if(bitmap != null){
            imageView.setImageBitmap(bitmap);
        }
        else{
            Toast.makeText(context,"下载图片失败",Toast.LENGTH.LONG).show();
        }
        //关闭进度对话框
        dialog.dismiss();
    }
}

public class MainActivity extends Activity{
    private ImageView imageView;
    private MyAsyncTask myAsyncTask;
    private String url = "";http://p5.qhimg.com/dmt/490_350_/t01405cf23f986e5ef6.jpg
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity);
        
        imageView = (ImageView)findViewById(R.id.iv_imageview);
        //创建AsyncTask的实例
        myAsyncTask = new MyAsyncTask(this,imageView);
    }
    
    //点击按钮,开始异步下载图片
    public void downloadPic(View view){
        /*AsyncTask的实例只能被执行一次*/
        myAsyncTask.execute(url);
    }
}

猜你喜欢

转载自blog.csdn.net/pangjl1982/article/details/85037405