public abstract class AsyncTask< Params, Progress, Result > 对于android中的处理异步线程的机制, 能够更加简单的处理异步任务. 如下图是该抽象类的大纲
其中,doInBackground(Params… params)是一个抽象方法,我们继承AsyncTask时必须覆写此方法;onPreExecute()、onProgressUpdate(Progress… values)、onPostExecute(Result result)、onCancelled()这几个方法体都是空的,我们需要的时候可以选择性的覆写它们;execute((Params… params)是触发一个实例task任务时调用的;publishProgress(Progress… values)是final修饰的,不能覆写,只能去调用,我们一般会在doInBackground(Params… params)中调用此方法;另外,我们可以看到有一个Status的枚举类和getStatus()方法,Status枚举类中记录了该AsyncTask任务的三种状态, AsyncTask的初始状态为PENDING,代表待定状态,RUNNING代表执行状态,FINISHED代表结束状态,我们可以通过这几种状态,对AsyncTask进行启动,终止等操作。
第二部分 介绍其基本使用
以打开百度首页为例子,介绍task的使用步骤:
第一步: 继承抽象类AsyncTask构建子类,必须覆写doInBackground(String… params) 方法,其他方法根据需要调用或者覆写,在doInBackground(String… params)方法中,触发异步请求,也就是请求 http://www.baidu.com , 并通过覆写onPreExecute(), 将异步请求执行之前的UI界面进行处理.
private class MyTask extends AsyncTask<String, Integer, String> {
//onPreExecute方法用于在执行后台任务前做一些UI操作
@Override
protected void onPreExecute() {
Log.i(TAG, "onPreExecute() called");
textView.setText("loading...");
}
//doInBackground方法内部执行后台任务,不可在此方法内修改UI
@Override
protected String doInBackground(String... params) {
Log.i(TAG, "doInBackground(Params... params) called");
try {
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(params[0]);
HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
long total = entity.getContentLength();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int count = 0;
int length = -1;
while ((length = is.read(buf)) != -1) {
baos.write(buf, 0, length);
count += length;
//调用publishProgress公布进度,最后onProgressUpdate方法将被执行
publishProgress((int) ((count / (float) total) * 100));
//为了演示进度,休眠500毫秒
Thread.sleep(500);
}
return new String(baos.toByteArray(), "gb2312");
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return null;
}
//onProgressUpdate方法用于更新进度信息
@Override
protected void onProgressUpdate(Integer... progresses) {
Log.i(TAG, "onProgressUpdate(Progress... progresses) called");
progressBar.setProgress(progresses[0]);
textView.setText("loading..." + progresses[0] + "%");
}
//onPostExecute方法用于在执行完后台任务后更新UI,显示结果
@Override
protected void onPostExecute(String result) {
Log.i(TAG, "onPostExecute(Result result) called");
textView.setText(result);
}
//onCancelled方法用于在取消执行中的任务时更改UI
@Override
protected void onCancelled() {
Log.i(TAG, "onCancelled() called");
textView.setText("cancelled");
progressBar.setProgress(0);
}
}
}
第二步:在UI线程中创建task实例 并触发
mTask = new MyTask();
mTask.execute(“http://www.baidu.com“);
public class MainActivity extends Activity {
private static final String TAG = "ASYNC_TASK";
private Button execute;
private Button cancel;
private ProgressBar progressBar;
private TextView textView;
private MyTask mTask;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
execute = (Button) findViewById(R.id.execute);
cancel = (Button) findViewById(R.id.cancel);
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
textView = (TextView) findViewById(R.id.text_view);
execute.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常
mTask = new MyTask();
mTask.execute("http://www.baidu.com");
}
});
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//取消一个正在执行的任务,onCancelled方法将会被调用 ,该方法只是将对应的task标记为cancel状态 并不是真正的取消线程的执行,线程在后台仍然继续执行 另 java中不能直接见到粗暴的结束某一个线程
mTask.cancel(true);
}
});
}
第三步: 与UI通信 通过onPostExecute(String result) 方法将异步请求的结果传递给UI线程,更新UI界面.
//onProgressUpdate方法用于更新进度信息
@Override
protected void onProgressUpdate(Integer... progresses) {
Log.i(TAG, "onProgressUpdate(Progress... progresses) called");
progressBar.setProgress(progresses[0]);
textView.setText("loading..." + progresses[0] + "%");
}
//onPostExecute方法用于在执行完后台任务后更新UI,显示结果
@Override
protected void onPostExecute(String result) {
Log.i(TAG, "onPostExecute(Result result) called");
textView.setText(result);
}
以该例子对该抽象类public abstract class AsyncTask< Params, Progress, Result > 的三种泛型参数做如下介绍:
Params 是“启动任务执行的输入参数” 触发异步任务执行的时候会调用execute(Params… params),去执行一个异步任务, 该方法将参数传递给后台真正异步线程,onPreExecute(),在execute(Params… params)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。
Progress 是“后台任务执行的进度”,在进行doInBackground的过程中,可以通过调用publishProgress(Progress… values)将异步执行的进度告诉UI线程,UI线程通过onProgressUpdate(Progress… values)将后台执行的进度公布在UI界面中
Result 是”后台计算结果的类型”当doInBackground执行完毕或者出现异常时,会通过onPostExecute(Result result)方法对加载的结果传递给UI线程,并更新UI界面.
例子中传入的url = “http://www.baidu.com“是String类型的,并使用(int) ((count / (float) total) * 100)来表示加载的进度,所以是Integer类型,最后将获取的结果以new String(baos.toByteArray(), “gb2312”)类型传递给UI界面的,
因此例子中的MyTask是以String为请求参数 以Integer为进度值,以String为结果的异步任务去执行的. 所以创建实力的时候是 private class MyTask extends AsyncTask< String, Integer, String >
在特定场合下,并不是所有类型都被使用,如果没有被使用,可以用java.lang.Void类型代替。
总结:
一个异步任务的执行一般包括以下几个步骤:
1.execute(Params… params),在主线程通过调用task的该方法执行一个异步任务
2.onPreExecute(),在execute(Params… params)被调用后立即执行,在执行后台任务前对UI做一些标记, 或者界面的改变。
3.doInBackground(Params… params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishProgress(Progress… values)来更新进度信息。
4.onProgressUpdate(Progress… values),在调用publishProgress(Progress… values)时,此方法被执行,直接将进度信息更新到UI组件上。
5.onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。
在使用的时候,有几点需要格外注意:
1.异步任务的实例必须在UI线程中创建。
2.execute(Params… params)方法必须在UI线程中调用。
3.onPreExecute(),doInBackground(Params… params),onProgressUpdate(Progress… values),onPostExecute(Result result)这几个方法。都是系统调用的,不能手动调用,只要执行execute即可触发一个异步任务的执行.
4.不能在doInBackground(Params… params)中更改UI组件的信息, 该方法实在其他线程中执行的,onPreExecute(),onProgressUpdate(Progress… values),onPostExecute(Result result)这几个方法承接了异步线程处理的结果,都是运行在UI线程中,可直接更新UI界面.
5.一个任务实例只能执行一次,如果执行第二次将会抛出RuntimeException异常,也就是例子中mTask.execute(“http://www.baidu.com“) 只能执行一次.
第三部分 介绍其工作原理
从第二部分可知,一个异步任务的触发是由execute开始的,从这里作为入口看看AsyncTask真正的工作流程
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
//如果该任务正在被执行则抛出异常
//值得一提的是,在调用cancel取消任务后,状态仍未RUNNING
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
//如果该任务已经执行完成则抛出异常
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
//改变状态为RUNNING
mStatus = Status.RUNNING;
//调用onPreExecute方法
onPreExecute();
mWorker.mParams = params;
sExecutor.execute(mFuture);
return this;
}
从execute的代码段中,可知,对Status.PENDING状态的task在会首先调用onPreExecute()方法,来标记UI界面的状态,之后将params传递给mWorker,并执行sExecutor.execute(mFuture);
其中 sExecutor是java.util.concurrent.ThreadPoolExecutor的实例,用于管理线程的执行, 也就是在线程池中创建一个新的线程,用于执行后台任务.
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 10;
//新建一个队列用来存放线程
private static final BlockingQueue<Runnable> sWorkQueue =
new LinkedBlockingQueue<Runnable>(10);
//新建一个线程工厂
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
//新建一个线程
public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
//新建一个线程池执行器,用于管理线程的执行
private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
mWorker实际上是AsyncTask的一个的抽象内部类,且实现对象Callable的实例,它实现了Callable< Result >接口中的call()方法,
mFuture实际上是java.util.concurrent.FutureTask的实例,下面是它的FutureTask类的相关信息:
public class FutureTask<V> implements RunnableFuture<V> {
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
所以从代码中可以看到 其实mFuture是Runnable的子类
mWorker和mFuture在AsyncTask中的体现如下, 在执行onPreExecute()方法,标记了主线程之后,将参数传递给mWorker,再通过ThreadPoolExecutor的实例sExecutor触发了以mWorker为参数的mFuture方法,mFuture实例中会调用mWorker做后台任务,而覆写call方法的mWorker实例是将该任务设置为后台级别,并调用doInBackground方法开始执行后台任务,后台任务完成后系统会调用done方法,将后台结果发送出去.
private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture;
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
//call方法被调用后,将设置优先级为后台级别,然后调用AsyncTask的doInBackground方法
public Result call() throws Exception {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return doInBackground(mParams);
}
};
//在mFuture实例中,将会调用mWorker做后台任务,完成后会调用done方法
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
Message message;
Result result = null;
try {
result = get();
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
//发送取消任务的消息
message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
message.sendToTarget();
return;
} catch (Throwable t) {
throw new RuntimeException("An error occured while executing "
+ "doInBackground()", t);
}
//发送显示结果的消息
message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(AsyncTask.this, result));
message.sendToTarget();
}
};
}
mFuture实例对象的done()方法中,通过不通的情况会发送不同的由AsyncTaskResult< Result>()构建的消息,将不同的结果发送出去.如果捕捉到了CancellationException类型的异常,则发送一条“MESSAGE_POST_CANCEL”的消息;如果顺利执行,则发送一条“MESSAGE_POST_RESULT”的消息,而消息都与一个sHandler对象关联。
这个sHandler实例实际上是AsyncTask内部类InternalHandler的实例,而InternalHandler正是继承了Handler
private static final int MESSAGE_POST_RESULT = 0x1; //显示结果
private static final int MESSAGE_POST_PROGRESS = 0x2; //更新进度
private static final int MESSAGE_POST_CANCEL = 0x3; //取消任务
private static final InternalHandler sHandler = new InternalHandler();
private static class InternalHandler extends Handler {
@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
//调用AsyncTask.finish方法
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
//调用AsyncTask.onProgressUpdate方法
result.mTask.onProgressUpdate(result.mData);
break;
case MESSAGE_POST_CANCEL:
//调用AsyncTask.onCancelled方法
result.mTask.onCancelled();
break;
}
}
}
由sHandler的代码可知,在sHandler实例对象的handleMessage(Message msg)方法里,使用AsyncTaskResult result = (AsyncTaskResult) msg.obj方式取得消息中附带的AsyncTaskResult消息
而AsyncTaskResult其实也是AsyncTask的一个内部类,是用来包装执行结果的一个类,让我们来看一下它的代码结构:
@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
可知这个AsyncTaskResult封装了一个AsyncTask的实例和某种类型的数据集.所以在发送消息时是将AsyncTask实例和后台结果result发送出去.
sHandler在处理消息时是如何使用这个task对象和数据集呢? 举例如下
result.mTask.finish(result.mData[0]);
result.mTask.onProgressUpdate(result.mData);
分析一:当InternalHandler类型的实例sHandler收到MESSAGE_POST_RESULT类型的AsyncTaskResult消息时,是通过传进来的AsyncTask.this来调用它自己的finish方法,代码如下,从finish方法中可以看到,其实是调动AsyncTask的onPostExecute(result)方法 显示结果.
private void finish(Result result) {
if (isCancelled()) result = null;
onPostExecute(result); //调用onPostExecute显示结果
mStatus = Status.FINISHED; //改变状态为FINISHED
}
总结
在调用execute(Params… params)方法后,execute方法会调用onPreExecute()方法,然后由ThreadPoolExecutor实例sExecutor执行一个带WorkerRunnable参数的FutureTask任务,这个过程中doInBackground(Params… params)将被调用,当后台任务结束以后,会调用FutureTask的done方法,将后台结果通过InternalHandler实例sHandler发送出去.发送的消息由AsyncTaskResult构建,包含了该异步实例task和后台任务的结果result. 对不同类型的消息,使用task实例调用不同的方法来完成与UI线程的通信.
1 MESSAGE_POST_PROGRESS消息:result.mTask.onProgressUpdate(result.mData);
如果被开发者覆写的doInBackground(Params… params)方法中调用了publishProgress(Progress… values)方法,则通过InternalHandler实例sHandler发送一条MESSAGE_POST_PROGRESS消息,更新进度,sHandler处理消息时onProgressUpdate(Progress… values)方法将被调用;
2 MESSAGE_POST_CANCEL消息:result.mTask.onCancelled();
如果遇到异常,则发送一条MESSAGE_POST_CANCEL的消息,取消任务,sHandler处理消息时onCancelled()方法将被调用;
3 MESSAGE_POST_RESULT消息:result.mTask.finish(result.mData[0]);
如果执行成功,则发送一条MESSAGE_POST_RESULT的消息,显示结果,sHandler处理消息时onPostExecute(Result result)方法被调用。
可知AsyncTask的本质就是对Thread+Handler的良好封装,(sExecutor,mFuture,mWorker 均属于Thread类,sHandler属于Handler类)减少了开发者处理问题的复杂度,提高了开发效率。