AsyncTask是
Android用来实现异步操作的一个类,因为被
Android创造时加入了android的平台特性,因此,
AsyncTask的异步操作相对于
Thread来说,更安全、方便和实用。但其实质上也是对
Thread的一个封装。与Handler很相似,都是为了使一些耗时操作不会堵塞android主线程并解决android的非主线程不能进行UI操作,而诞生的。
AsyncTask可以很方便的执行异步操作(doinBackground()),又能很方便的与主线程进行通讯,而且其又有很好的封装性,所以能够进行取消(cancel())操作。
下面给出示例代码:通过网络下载一张图片并显示。
package com.cao.asynctasktest;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
/**
* @author caohuize
* AysncTaskDemo 通过网络下载一个图片的状态来学习AysncTask
*/
public class MainActivity extends Activity {
private static final String ImageUrl = "http://i1.cqnews.net/sports/attachement/jpg/site82/2011-10-01/2960950278670008721.jpg";
private ProgressBar mProgressBar;
private ImageView mImageView;
private Button mGetImage;
private Button mAbort;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_main);
mProgressBar = (ProgressBar) findViewById(R.id.async_task_progress);
mImageView = (ImageView) findViewById(R.id.async_task_displayer);
final ImageLoader loader = new ImageLoader();
mGetImage = (Button) findViewById(R.id.async_task_get_image);
mGetImage.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
/*开启异步线程操作*/
Log.i("cao", "user call execute!");
loader.execute(ImageUrl);
}
});
mAbort = (Button) findViewById(R.id.asyc_task_abort);
mAbort.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
/*取消这个异步线程操作,销毁线程*/
loader.cancel(true);
}
});
mAbort.setEnabled(false);
}
private class ImageLoader extends AsyncTask<String, Integer, Bitmap> {
private static final String TAG = "ImageLoader";
/**
*这是用户调用execute()方法之后最先调用的方法
**/
@Override
protected void onPreExecute() {
// Initialize progress and image
Log.i("cao", "call onPreExecute begin!");
mGetImage.setEnabled(false);
mAbort.setEnabled(true);
mProgressBar.setVisibility(View.VISIBLE);
mProgressBar.setProgress(0);
mImageView.setImageResource(R.drawable.ic_launcher);
}
/**
*经过 onPreExecute 方法的准备之后,进入耗时操作方法
**/
@Override
protected Bitmap doInBackground(String... url) {
Log.i("cao", "call doInBackground begin!");
//一些网络操作代码,通过网络下载图片保存后并转化为Bitmap
try {
URL u;
HttpURLConnection conn = null;
InputStream in = null;
OutputStream out = null;
final String filename = "local_temp_image";
try {
u = new URL(url[0]);
conn = (HttpURLConnection) u.openConnection();
conn.setDoInput(true);
conn.setDoOutput(false);
conn.setConnectTimeout(20 * 1000);
in = conn.getInputStream();
out = openFileOutput(filename, Context.MODE_PRIVATE);
byte[] buf = new byte[8196];
int seg = 0;
final long total = conn.getContentLength();
long current = 0;
/*
* Without checking isCancelled(), the loop continues until reading whole image done, i.e. the progress
* continues go up to 100. But onPostExecute() will not be called.
* By checking isCancelled(), we can stop immediately, i.e. progress stops immediately when cancel() is called.
*/
while (!isCancelled() && (seg = in.read(buf)) != -1) {
out.write(buf, 0, seg);
current += seg;
int progress = (int) ((float) current / (float) total * 100f);
publishProgress(progress);
SystemClock.sleep(1000);
}
} finally {
if (conn != null) {
conn.disconnect();
}
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
}
return BitmapFactory.decodeFile(getFileStreamPath(filename).getAbsolutePath());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
*开始耗时操作之后,开始跟新用户进度的方法
**/
@Override
protected void onProgressUpdate(Integer... progress) {
Log.i("cao", "call onProgressUpdate begin!");
mProgressBar.setProgress(progress[0]);
}
/**
*当耗时操作方法完之后,调用修改UI的方法。
**/
@Override
protected void onPostExecute(Bitmap image) {
Log.i("cao", "call onPostExecute begin!");
if (image != null) {
mImageView.setImageBitmap(image);
}
mProgressBar.setProgress(100);
mProgressBar.setVisibility(View.GONE);
mAbort.setEnabled(false);
}
}
}
这里是配置文件的代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<!-- 分别显示 下载进度的显示-->
<ProgressBar
android:id="@+id/async_task_progress"
android:layout_width="match_parent"
android:layout_height="20dp"
style="?android:attr/progressBarStyleHorizontal"
android:max="100"
/>
<!-- 分别显示 下载前 下载中 下载后的显示 -->
<ImageView
android:id="@+id/async_task_displayer"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginTop="20dp"
android:layout_gravity="center_horizontal"
android:background="@drawable/public_head_boy"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="20dp"
android:layout_gravity="center_horizontal"
>
<Button
android:id="@+id/async_task_get_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="get the image"/>
<Button
android:id="@+id/asyc_task_abort"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="abort"/>
</LinearLayout>
</LinearLayout>
AsyncTask的方法
必选方法:
1.doinBackground(params) 后台操作,程序中耗时的操作可以放在这个方法里面,不能操作UI, Params 是任务执行器需要的数据类型。其Params是可变参数。
2. onpostexecute(results)相当于handler处理UI的方式,在这里可以使用在doinbackground得到的结果处理操作UI,在此异步操作线程。此方法在主线程执行,任务执行的结果作为此方法的参数返回。其results也是可变参数。
可选方法:
1、 onprogressupdate(progress…)可以使用进度条增加用户体验度。此方法在主线程执行,用户显示任务执行的进度。progress也是可变参数,可以用来查看异步线程的进度。
2、 onpreExecute() 这里是最新用户调用excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
3、 onCancelled() 用户调用取消时,要做的操作。
另外,Google还给AsyncTask制定了一些限制:
1.AsyncTask的实例必须是在主线程即UI线程中创建
2. AsyncTask的实例的Execute()方法必须在主线程中调用。(一般是在需要进行异步线程操作时调用)
3.不要手动调用AsyncTask里面的几个方法。
4. AsyncTask对象不可重复使用,也就是说一个AsyncTask对象只能execute()一次,否则会有异常抛出"java.lang.IllegalStateException:Cannot execute task: the task is already running"
5.在doInBackground()中要检查isCancelled()的返回值,如果你的异步任务是可以取消的话。
注:cancel()仅仅是给AsyncTask对象设置了一个标识位,当调用了cancel()后,发生的事情只有:AsyncTask对象的标识位变了,和doInBackground()执行完成后,onPostExecute()不会被回调了,而doInBackground()和onProgressUpdate()还是会继续执行直到doInBackground()结束。所以要在doInBackground()中不断的检查isCancellled()的返回值,当其返回true时就停止执行,特别是有循环的时候。如上面的例子,如果把读取数据的isCancelled()检查去掉,图片还是会下载,进度也一直会走,只是最后图片不会放到UI上(因为onPostExecute()没被回调)!