细谈AsyncTask

原创
2015/01/18 19:22
阅读数 318

android AsyncTask介绍

AsyncTask和Handler对比

1 ) AsyncTask实现的原理,和适用的优缺点

AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程.

使用的优点:

l  简单,快捷

l  过程可控   

使用的缺点:

l  在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来.

2 )Handler异步实现的原理和适用的优缺点

在Handler 异步实现时,涉及到 Handler, Looper, Message,Thread四个对象,实现异步的流程是主线程启动Thread(子线程)àthread(子线程)运行并生成Message-àLooper获取Message并传递给HandleràHandler逐个获取Looper中的Message,并进行UI变更。

使用的优点:

l  结构清晰,功能定义明确

l  对于多个后台任务时,简单,清晰

AsyncTask介绍

Android的AsyncTask比Handler更轻量级一些,适用于简单的异步处理。

首先明确Android之所以有Handler和AsyncTask,都是为了不阻塞主线程(UI线程),且UI的更新只能在主线程中完成,因此异步处理是不可避免的。

Android为了降低这个开发难度,提供了AsyncTask。AsyncTask就是一个封装过的后台任务类,顾名思义就是异步任务。

AsyncTask直接继承于Object类,位置为android.os.AsyncTask。要使用AsyncTask工作我们要提供三个泛型参数,并重载几个方法(至少重载一个)。

AsyncTask定义了三种泛型类型 Params,Progress和Result。

  • Params 启动任务执行的输入参数,比如HTTP请求的URL。

  • Progress 后台任务执行的百分比。

  • Result 后台执行任务最终返回的结果,比如String,Bitmap等。

使用过AsyncTask 的同学都知道一个异步加载数据最少要重写以下这两个方法:

  • doInBackground(Params…) 后台执行,比较耗时的操作都可以放在这里。注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress…)来更新任务的进度。

  • onPostExecute(Result)  相当于Handler 处理UI的方式,在这里面可以使用在doInBackground 得到的结果处理操作UI。 此方法在主线程执行,任务执行的结果作为此方法的参数返回

有必要的话你还得重写以下这三个方法,但不是必须的:

  • onProgressUpdate(Progress…)   可以使用进度条增加用户体验度。 此方法在主线程执行,用于显示任务执行的进度。

  • onPreExecute()        这里是最终用户调用Excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。

  • onCancelled()             用户调用取消时,要做的操作

使用AsyncTask类,以下是几条必须遵守的准则:

  • Task的实例必须在UI thread中创建;

  • execute方法必须在UI thread中调用;

  • 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;

  • 该task只能被执行一次,否则多次调用时将会出现异常;

介绍一个简单例子。了解一下 AsyncTask的运行过程。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/textView01"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <ProgressBar
        android:id="@+id/progressBar02"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <Button
        android:id="@+id/button03"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="更新progressbar" />
</LinearLayout>

MainActivity.java:

public class MainActivity extends Activity {
 private Button button;
 private ProgressBar progressBar;
 private TextView textView;
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  button = (Button) findViewById(R.id.button03);
  progressBar = (ProgressBar) findViewById(R.id.progressBar02);
  textView = (TextView) findViewById(R.id.textView01);
  button.setOnClickListener(new OnClickListener() {
   @Override
   public void onClick(View v) {
    ProgressBarAsyncTask asyncTask = new ProgressBarAsyncTask(
      textView, progressBar);
    asyncTask.execute(1000);
   }
  });
 }
}

 

ProgressBarAsyncTask.java:

/**
 * 生成该类的对象,并调用execute方法之后 首先执行的是onProExecute方法 其次执行doInBackgroup方法
 * 
 */
public class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {
 private TextView textView;
 private ProgressBar progressBar;
 public ProgressBarAsyncTask(TextView textView, ProgressBar progressBar) {
  super();
  this.textView = textView;
  this.progressBar = progressBar;
 }
 /**
  * 这里的Integer参数对应AsyncTask中的第一个参数 这里的String返回值对应AsyncTask的第三个参数
  * 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改
  * 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作
  */
 @Override
 protected String doInBackground(Integer... params) {
  NetOperator netOperator = new NetOperator();
  int i = 0;
  for (i = 10; i <= 100; i += 10) {
   netOperator.operator();
   publishProgress(i);
  }
  return i + params[0].intValue() + "";
 }
 /**
  * 这里的String参数对应AsyncTask中的第三个参数(也就是接收doInBackground的返回值)
  * 在doInBackground方法执行结束之后在运行,并且运行在UI线程当中 可以对UI控件进行设置
  */
 @Override
 protected void onPostExecute(String result) {
  textView.setText("异步操作执行结束" + result);
 }
 // 该方法运行在UI线程当中,并且运行在UI线程当中 可以对UI控件进行设置
 @Override
 protected void onPreExecute() {
  textView.setText("开始执行异步线程");
 }
 /**
  * 这里的Intege参数对应AsyncTask中的第二个参数
  * 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行
  * onProgressUpdate是在UI线程中执行,所有可以对UI控件进行操作
  */
 @Override
 protected void onProgressUpdate(Integer... values) {
  int value = values[0];
  progressBar.setProgress(value);
  textView.setText("执行异步线程"+value);
 }
}

NetOperator.java:

public class NetOperator {

 public void operator() {

  try {

   // 休眠1秒

   Thread.sleep(1000);

  } catch (InterruptedException e) {

   // TODO Auto-generated catch block

   e.printStackTrace();

  }

 }

}

 

再以开发瀑布流照片为例子。里面涉及的知识很广,大家好好斟酌看看。

/**
  * 异步下载图片的任务。
  * 
  * @author guolin
  */
 class LoadImageTask extends AsyncTask<String, Void, Bitmap> {
  /**
   * 图片的URL地址
   */
  private String mImageUrl;
  /**
   * 可重复使用的ImageView
   */
  private ImageView mImageView;
  public LoadImageTask() {
  }
  /**
   * 将可重复使用的ImageView传入
   * 
   * @param imageView
   */
  public LoadImageTask(ImageView imageView) {
   mImageView = imageView;
  }
  @Override
  protected Bitmap doInBackground(String... params) {
   mImageUrl = params[0];
   Bitmap imageBitmap = imageLoader.getBitmapFromMemoryCache(mImageUrl);
   if (imageBitmap == null) {
    imageBitmap = loadImage(mImageUrl);
   }
   return imageBitmap;
  }
  @Override
  protected void onPostExecute(Bitmap bitmap) {
   if (bitmap != null) {
    double ratio = bitmap.getWidth() / (columnWidth * 1.0);
    int scaledHeight = (int) (bitmap.getHeight() / ratio);
    addImage(bitmap, columnWidth, scaledHeight);
   }
   taskCollection.remove(this);
  }
  /**
   * 根据传入的URL,对图片进行加载。如果这张图片已经存在于SD卡中,则直接从SD卡里读取,否则就从网络上下载。
   * 
   * @param imageUrl
   *            图片的URL地址
   * @return 加载到内存的图片。
   */
  private Bitmap loadImage(String imageUrl) {
   File imageFile = new File(getImagePath(imageUrl));
   if (!imageFile.exists()) {
    downloadImage(imageUrl);
   }
   if (imageUrl != null) {
    Bitmap bitmap = ImageLoader.decodeSampledBitmapFromResource(imageFile.getPath(),
      columnWidth);
    if (bitmap != null) {
     imageLoader.addBitmapToMemoryCache(imageUrl, bitmap);
     return bitmap;
    }
   }
   return null;
  }
  /**
   * 向ImageView中添加一张图片
   * 
   * @param bitmap
   *            待添加的图片
   * @param imageWidth
   *            图片的宽度
   * @param imageHeight
   *            图片的高度
   */
  private void addImage(Bitmap bitmap, int imageWidth, int imageHeight) {
   LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(imageWidth,
     imageHeight);
   if (mImageView != null) {
    mImageView.setImageBitmap(bitmap);
   } else {
    ImageView imageView = new ImageView(getContext());
    imageView.setLayoutParams(params);
    imageView.setImageBitmap(bitmap);
    imageView.setScaleType(ScaleType.FIT_XY);
    imageView.setPadding(5, 5, 5, 5);
    imageView.setTag(R.string.image_url, mImageUrl);
    imageView.setOnClickListener(new OnClickListener() {
     @Override
     public void onClick(View v) {
      Intent intent = new Intent(getContext(), ImageDetailsActivity.class);
      intent.putExtra("image_path", getImagePath(mImageUrl));
      getContext().startActivity(intent);
     }
    });
    findColumnToAdd(imageView, imageHeight).addView(imageView);
    imageViewList.add(imageView);
   }
  }
  /**
   * 找到此时应该添加图片的一列。原则就是对三列的高度进行判断,当前高度最小的一列就是应该添加的一列。
   * 
   * @param imageView
   * @param imageHeight
   * @return 应该添加图片的一列
   */
  private LinearLayout findColumnToAdd(ImageView imageView, int imageHeight) {
   if (firstColumnHeight <= secondColumnHeight) {
    if (firstColumnHeight <= thirdColumnHeight) {
     imageView.setTag(R.string.border_top, firstColumnHeight);
     firstColumnHeight += imageHeight;
     imageView.setTag(R.string.border_bottom, firstColumnHeight);
     return firstColumn;
    }
    imageView.setTag(R.string.border_top, thirdColumnHeight);
    thirdColumnHeight += imageHeight;
    imageView.setTag(R.string.border_bottom, thirdColumnHeight);
    return thirdColumn;
   } else {
    if (secondColumnHeight <= thirdColumnHeight) {
     imageView.setTag(R.string.border_top, secondColumnHeight);
     secondColumnHeight += imageHeight;
     imageView.setTag(R.string.border_bottom, secondColumnHeight);
     return secondColumn;
    }
    imageView.setTag(R.string.border_top, thirdColumnHeight);
    thirdColumnHeight += imageHeight;
    imageView.setTag(R.string.border_bottom, thirdColumnHeight);
    return thirdColumn;
   }
  }
  /**
   * 将图片下载到SD卡缓存起来。
   * 
   * @param imageUrl
   *            图片的URL地址。
   */
  private void downloadImage(String imageUrl) {
   if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
    Log.d("TAG", "monted sdcard");
   } else {
    Log.d("TAG", "has no sdcard");
   }
   HttpURLConnection con = null;
   FileOutputStream fos = null;
   BufferedOutputStream bos = null;
   BufferedInputStream bis = null;
   File imageFile = null;
   try {
    URL url = new URL(imageUrl);
    con = (HttpURLConnection) url.openConnection();
    con.setConnectTimeout(5 * 1000);
    con.setReadTimeout(15 * 1000);
    con.setDoInput(true);
    con.setDoOutput(true);
    bis = new BufferedInputStream(con.getInputStream());
    imageFile = new File(getImagePath(imageUrl));
    fos = new FileOutputStream(imageFile);
    bos = new BufferedOutputStream(fos);
    byte[] b = new byte[1024];
    int length;
    while ((length = bis.read(b)) != -1) {
     bos.write(b, 0, length);
     bos.flush();
    }
   } catch (Exception e) {
    e.printStackTrace();
   } finally {
    try {
     if (bis != null) {
      bis.close();
     }
     if (bos != null) {
      bos.close();
     }
     if (con != null) {
      con.disconnect();
     }
    } catch (IOException e) {
     e.printStackTrace();
    }
   }
   if (imageFile != null) {
    Bitmap bitmap = ImageLoader.decodeSampledBitmapFromResource(imageFile.getPath(),
      columnWidth);
    if (bitmap != null) {
     imageLoader.addBitmapToMemoryCache(imageUrl, bitmap);
    }
   }
  }
  /**
   * 获取图片的本地存储路径。
   * 
   * @param imageUrl
   *            图片的URL地址。
   * @return 图片的本地存储路径。
   */
  private String getImagePath(String imageUrl) {
   int lastSlashIndex = imageUrl.lastIndexOf("/");
   String imageName = imageUrl.substring(lastSlashIndex + 1);
   String imageDir = Environment.getExternalStorageDirectory().getPath()
     + "/PhotoWallFalls/";
   File file = new File(imageDir);
   if (!file.exists()) {
    file.mkdirs();
   }
   String imagePath = imageDir + imageName;
   return imagePath;
  }
 }

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
打赏
0 评论
3 收藏
0
分享
返回顶部
顶部