网络编程之AsyncTask使用总结

官方概述

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线程,而且不需要手动的操作threadhandler
AsyncTask并不是一个通常的线程框架,而是为Thread和Handler设计的帮助类。AsyncTask最适合用于耗时较短(最多几秒钟)的操作。如果需要在线程中执行长时间的操作,强烈推荐使用java.util.concurrent包提供的API,比如Executor,TreadPoolExecutorFutureTask
一个AsyncTask可以把操作的执行过程放到子线程中执行,把执行结果发布到UI线程。定义一个AsyncTask需要指定Params,Progress,Result三个泛型的类型,在执行过程中将调用onPreExecute,doInBackground, onProgressUpdateonPostExecute四个方法。

使用

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线程中执行。

使用注意事项
  1. AsyncTask类必须加载在UI thread。(Android系统会自动完成,不需要管)
  2. AsyncTask类的必须在UI thread中实例化。(因为AsyncTask中的Handler必须属于UI thread,AsyncTask才能和UI thread中执行)
  3. execute(Params...) 必须在UI thread中被调用。
  4. 不要手动调用 onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...) 方法,这些方法的调用由AsyncTask自定完成
  5. 一个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的引用,将直接导致程序崩溃。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容