官方概述
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.
An asynchronous task is defined by a computation that runs on a background thread and whose result is published on the UI thread. An asynchronous task is defined by 3 generic types, called Params, Progress and Result, and 4 steps, called onPreExecute, doInBackground, onProgressUpdate and onPostExecute.
翻译:AsyncTask可以恰当方便的操作UI线程。它可以执行后台任务,并把任务结果发布到UI线程,而且不需要手动的操作thread和handler。
AsyncTask并不是一个通常的线程框架,而是为Thread和Handler设计的帮助类。AsyncTask最适合用于耗时较短(最多几秒钟)的操作。如果需要在线程中执行长时间的操作,强烈推荐使用java.util.concurrent包提供的API,比如Executor,TreadPoolExecutor和FutureTask。
一个AsyncTask可以把操作的执行过程放到子线程中执行,把执行结果发布到UI线程。定义一个AsyncTask需要指定Params,Progress,Result三个泛型的类型,在执行过程中将调用onPreExecute,doInBackground, onProgressUpdate 和 onPostExecute四个方法。
使用
AsyncTask为抽象类,使用前必须先定义一个它的子类,实现doInBackground()方法,为Params,Progress,Result指定确切的参数。
实现说明
Type | 说明 |
---|---|
Params | 任务所需参数的类型,变长参数;在调用任务执行方法execute()时,作为execute的参数使用。若不使用,指定为Void |
Progress | 说明任务执行的进度的类型,如Integer等;若不使用,指定为Void |
Result | 任务完成后返回的结果的类型,若不使用,指定为Void |
方法调用顺序
顺序 | 方法 | 运行位置 | 必须实现 | 作用 |
---|---|---|---|---|
1.Task执行前 | onPreExecute() | UI Thread | 否 | 在Task执行前调用(即,doInBackground()法前),可用于做一些任务执行前的操作,如显示一个ProgressDialog说明进度 |
2.Task执行中 | doInBackground(Params...) | background Thread | 是 | 用于执行Task,在此方法中,可以调用publishProgress(Progress...) 发布Task实时进度信息,在onProgressUpdate(Progress...)接收到发布的信息 |
3.Task执行中 | onProgressUpdate(Progress...) | UI Thread | 否 | 接收publishProgress(Progress...)发布的Task进度信息,可以在这里更新显示进度的界面, |
4.Task执行完成后 | onPostExecute(Result) | UI Thread | 否 | 用于接收Task执行的结果,在UI thread中执行 |
官方提供的示例
//定义一个AysncTask类的子类
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
//调用方式
new DownloadFilesTask().execute(url1, url2, url3);
在这个示例中,定义了一个AsyncTask的子类DownloadFilesTask,其中URL对应Params,Integer表示进度Progress,Long为执行结果Result的类型。
doInBackground()方法体的代码将在子线程中执行。
publishProgress()方法用来发布任务执行进度情况,此方法被调用后,会唤起在UI线程中执行的onProgresssUpdate()方法,并接收publishProgress()发布的信息,我们可以根据这些信息更新我们的view等。
当后台任务执行完成后,onPostExecute()方法会被调用,此方法也是在UI线程中执行。
使用注意事项
- AsyncTask类必须加载在UI thread。(Android系统会自动完成,不需要管)
- AsyncTask类的必须在UI thread中实例化。(因为AsyncTask中的Handler必须属于UI thread,AsyncTask才能和UI thread中执行)
- execute(Params...) 必须在UI thread中被调用。
- 不要手动调用 onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) 方法,这些方法的调用由AsyncTask自定完成
- 一个AsyncTask的实例只能执行一次,即,execute(Params...)方法只能调用一次
取消Task
官方说明:
A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of **onPostExecute(Object) **will be invoked after doInBackground(Object[]) returns. To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.)
翻译:你可以在任何时候通过调用cancel(boolean)取消Task,cancel(boolean)方法被调用后,调用isCancelled()方法将返回True,并且在doInBackground(Object[])返回后,不在调用onPostExecute(Object) ,而是调用 onCancelled(Object)。为了保证Task尽可能快的被取消掉,你应该在doInBackground(Object[])方法中不停的查询 isCancelled() 的返回值,最好在方法中实现一个循环。
我们来看一下cancel(boolean)的源码
private final AtomicBoolean mCancelled = new AtomicBoolean();
public final boolean cancel(boolean mayInterruptIfRunning) {
mCancelled.set(true);
return mFuture.cancel(mayInterruptIfRunning);
}
cancle(boolean)只是将mCancelled的值置为true,而任是真正被取消的操作,要在doInBackground(Params)方法中通过查询mCancelled的值为true时才进行取消。
官方指出最好在doInBackground(Object[])中实现一个循环,用来保证尽快收到任务取消的信息。代码如下:
@Override
protected Void doInBackground(Integer... params) {
while (isCancelled()){
//do something
}
return null;
}
注意
但是有时候,我们无法使用循环,比如读取文件、请求网络等。如果你调用了cancel(false),任务会继续执行,直到任务完成,但onPostExecute()将不被会被调用,而是调用onCancel()。但我们的初衷是想让任务马上停止,而不是继续执行到完成,所以我们的程序做了无用功。如果使用cancel(true) 方法,即,参数mayInterruptIfRunning被置为true,那将提前中断我们的任务,但是如果我们的方法是不可中断的,例如BitmapFactory.decodeStream()方法,那么任务将继续执行。你可以过早的关闭这个流(stream)并捕获它抛出的异常,但这并不是通过调用cancel(true)完成的,因此cancel(true)就变的没有意义了。
执行顺序:多个AsyncTask实例之间的执行顺序
在Android 1.6之前AsyncTask是顺序执行的,在一个App中,只有前一个Task执行完成后, 下一个AsyncTask才能执行。
在Android 1.6-Android2.3中,由一个线程池来管理多个线程,同一时刻,可以有多个Task在执行。
从Android3.0开始,Google为了避免并行执行给多个应用带来的错误,有改为了顺序执行。
版本 | 执行顺序 |
---|---|
Android 1.6之前 | 顺序执行: |
Android 1.6-Android2.3 | 并行 |
Android 3.0-至今 | 顺序执行 |
当然我们可以通过调用 executeOnExecutor(java.util.concurrent.Executor, Params...)而非execute(Params..)方法来完成并行执行。
比如:
aAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
可能发生的内存泄露(Memory leaks)
我们可能有这样的错觉:当Activity被销毁时,其创建的AsyncTask也同样会被销毁------其实,并不然!
当Activity销毁后,AsyncTask继续执行直到完成。如果你在Activity#onDestroy()中调用了 cancel(boolean)方法,它会继续执行onCancelled(Result result)方法,如果你没有调用 cancel(boolean)方法,它会执行onPostExecute(Result) 方法。
假如在Activity被销毁前,我们没有调用AsyncTask#cancel(boolean)方法。这可能引起内存泄露,导致程序崩溃。比如你在onPostExecute(Result) 中对Activity的View进行操作,但是这些View已经随着Activity的销毁不再存在。所以,我们要确保在Activity销毁前,取消掉Task。
因为异步任务有方法在background Thread 中执行(doInBackground()),并且也有方法在UI线程中执行(比如,onPostExecute());在Task运行阶段,它将持有Activity的引用。但是如果Activity已经被销毁,它仍然持有这个的内存的引用。如何在后续的操作中,使用这个已经销毁的Activity的引用,将直接导致程序崩溃。
网友评论