首先,非常有必要贴点最原始东西,马上就会知道为什么 : )
AsyncTask
java.lang.Object | |
↳ | android.os.AsyncTask<Params, Progress, Result> |
进入正题:概述
异步任务使访问UI线程的过程变得更加简单和适当。AsyncTask允许用户在不新开线程和使用Handler的情况下,进行后台操作,并将结果直接发布到UI线程中。
异步任务类被设计作为Thread类和Handler类的帮助类,而不是组成线程框架的一部分(AsyncTask直接继承自Object根类,其实内部还是线程基于框架实现的)。异步任务应该用于处理短时操作(最多几秒的执行时间),如果你要让当前线程保持长时间运行,那么强烈建议你使用java.util.concurrent 包下的Executor, ThreadPoolExecutor 和 FutureTask 等API类。(可参考:http://www.iteye.com/topic/366591 ,http://dongxuan.iteye.com/blog/901689)
异步任务通过计算机的后台线程生成,异步任务的结果发布在UI线程中,一个异步任务的声明过程包含三种基本类型(Param,Progress, Result)和四个步骤(onPreExecute, doInBackground, onProgressUpdate 和 onPostExecute)
开发指南
获取更多有关任务和线程的信息,可阅读文档的 Processes and Threads 部分开发指南(可参考:http://serryzhao.iteye.com/blog/1291900)。
使用实例
异步任务必须作为子类使用,子类至少应该重写doInBackground(Params...)方法,通常还会重写另外一个onPostExecute(Result)方法。下面是一个子类的实例:
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);
异步任务的基本类型
异步任务使用的三个基本类型如下:
1. Params, 被传递到任务中执行的参数类型。
2. Progress, 后台计算过程中发布到UI线程的进度单元类型。
3. Result, 后台计算的结果类型。
通常异步任务并不会使用全部的参数类型,如果不使用该类型,则可以简单地使用Void表示。
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
四个步骤
一个异步任务的执行过程会经过下列四个步骤:
1. onPreExecute(), 异步任务执行前在UI线程中调用该方法。这个步骤通常用于做准备, 如:在UI线程中展示一个进度条。
2. doInBackground(Params...), 在onPreExecute()执行结束后立刻由后台线程调用。这个步骤通常用于进行比较长时的后台计算。异步任务的参数在这里传递到后台。计算的结果必须通过这一步返回,并在最后一步返回到UI线程中。在进度条例子中,这一步骤中还可以通过publishProgress(Progress...)发布更多的进度单元值。这些值会通过步骤onProgressUpdate(Progress...)被发布到主线程中。
3. onProgressUpdate(Progress...), 这一步会在调用 publishProgress(Progress...)后在主线程中运行,执行的进度会在这一步中更行。如果后台计算仍在进行,那么这一步可以在界面上展示任何形式的进度,如,通过动画形式的进度条在文本域中展示日志。
4. onPostExecute(Result), 后台计算完成后,在主线程中调用,后台计算的结果会在这一步中作为参数返回。
取消异步任务
通过调用cancel(boolean)可以在任何时候取消一个异步任务。调用这个方法后会调用isCancelled()返回一个true值。 如果调用cancel(boolean)方法,那么doInBackground(Object[]) 方法返回后会调用 onCancelled(Object)而不是onPostExecute(Object) 。如果要尽快确认一个异步任务是否被取消了,你应该周期性地在doInBackground(Object[])中检查isCancelled()的返回值 。
线程规则
下面是一些异步任务运行过程中必须要注意的线程规则:
* 异步任务类必须在主线程中加载,在android4.1 JELLY_BEAN版本中,这是自动进行的。
* 异步任务的实例必须在UI线程中调用。
* execute(Params...)方法必须在主线程中调用。
* 不要主动调用 onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...)等方法。
* 异步任务只会被执行一次(如果重复执行,则会抛出异常)
内存监督
异步任务确保所有的回调方法都是同步的,所以在不明确指明同步的情况下,下列操作都是线程安全的。
* 在构造方法或onPreExecute()中设置成员属性,并在doInBackground(Params...).中引用这些属性。
* 在doInBackground(Params...)中设置成员属性,并在 onProgressUpdate(Progress...)和onPostExecute(Result)中引用这些属性。
执行顺序
首次引用时, 异步任务会在一个单一的后台线程中有序执行。从DONUT版本开始,异步任务被放到线程池中执行,并允许多任务并行执行。从HONEYCOMB版本开始,异步任务在独立的线程池中执行,以避免异步任务并行执行导致的公共应用错误。
如果你确实想让异步任务并行执行,那么你可以通过THREAD_POOL_EXECUTOR 调用 executeOnExecutor(java.util.concurrent.Executor, Object[])。
更多详情分析和源码理解可以参考这些文章;