AsyncTask

为什么要用AsyncTask

任务线程Thread执行完耗时操作后,进行UI更新,通常的做法是通过Handler投递一个消息给UI线程,然后更新UI。这种方式对于整个过程的控制比较精细,但是也有缺点:(1) 代码相对臃肿;(2) 在多个任务同时执行时,不易对线程进行精确控制。

为了简化操作,系统提供AsyncTask工具类,对用户隐藏Thread、Runnable、Handler等相关对象,使的创建异步任务更加简单,只须重写相关方法即可实现后台操作和UI更新。

AsyncTask使用

可实现的函数

AsyncTask的定义:

public abstract class AsyncTask<Params, Progress, Result>{}

说明:3种泛型类型分别表示参数类型、后台任务执行的进度类型、返回的结果类型。如果不需要某个参数,可以设置为Void类型。

一个异步任务的执行一般包括以下几个步骤:
(1)execute(Params... params)
执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行。
(2)onPreExecute()
在execute被调用后立即执行,在UI线程中执行,一般用于后台任务执行前对UI做一些标记。
(3)doInBackground(Params... params)
在onPreExecute()完成后立即执行,用于执行较为耗时的操作。在执行过程中可以调用publishProgress来更新进度信息。
(4)onProgressUpdate(Progress... values)
执行在UI线程,在调用publishProgress时,此方法被执行,可以将进度信息更新到UI组件上。
(5)onPostExecute(Result result)
当后台任务执行结束后,调用此方法,将计算结果作为参数,在UI线程上进行相关结果的UI更新。

常用公共API
  • execute (Params... params)
    用指定的参数来执行此任务,此方法必须在UI线程中调用,因为要将handler绑定到主线程的Looper。
  • executeOnExecutor(Executor exec,Params... params)
    用指定的参数来执行此任务,指定运行的线程池。
  • getStatus ()
    获得任务的当前状态PENDING(等待执行)、RUNNING(正在运行)、FINISHED(运行完成)。
  • cancel (boolean mayInterruptIfRunning)
    尝试取消这个任务的执行,如果这个任务已经结束或者已经取消或者不能被取消或者某些其他原因,那么将导致这个操作失败。
  • isCancelled ()
    在任务正常结束之前能成功取消任务则返回true,否则返回false。
注意点

1)AsyncTask不与任何组件绑定生命周期
在Activity/或者Fragment中创建执行AsyncTask时,最好在Activity/Fragment的onDestory()调用 cancel(true);

2)内存泄漏
如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。

3)屏幕旋转
屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。

4)串行或者并行的执行异步任务
目前AsyncTask支持并行和串行的执行异步任务,当想要串行执行时,直接执行execute()方法,如果需要并行执行,则要执行executeOnExecutor(Executor exec, Object... params)。

5)AsyncTask的实例必须在UI线程中创建,execute方法必须在UI线程中调用。一个任务实例只能执行一次,执行多次会抛出异常。不能在doInBackground中更新UI组件的信息;

AsyncTask实现

AsyncTask机制

AsyncTask实现原理如下所示:


AsyncTask内部原理.jpg
Params与任务包装

WorkerRunnable定义:

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
}

FutureTask定义:

public class FutureTask<V> implements RunnableFuture<V> {
    private Callable<V> callable;
 
    public FutureTask(Callable<V> callable) {
        //必须传入非空的实现CallBack接口的工作线程WorkRunnable
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;
    }
    ...
}

Params、WorkerRunnable、FutureTask建立关联:

public abstract class AsyncTask<Params, Progress, Result> { 
    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;
    
    mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                //设置为true,代表当前方法被调用过
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    //设置线程的优先级
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    
                    //将任务参数传递给doInBackground方法处理
                    result = doInBackground(mParams);  
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    // 处理执行结果
                    postResult(result);
                }
                return result;
            }
        };
    
        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };

    public final AsyncTask<Params, Process, Result> execute(Params... params){
        return executeOnExecutor(sDefaultExecutor, params);
    }

    public final AsyncTask<Params, Process, Result> executeOnExecutor(Executor exec, Params... params){
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
                // 任务处于执行阶段或者完成阶段,再调用execute抛出异常
                case 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)");
            }
        }
        
        mStatus = Status.RUNNING; //标记为运行状态
        onPreExecute(); //准备工作的方法      
        mWorker.mParams = params; //任务执行参数-传递给WorkRunnable
 
        //默认线程池或者指定的线程池,执行execute(),传入FuturTask对象
        exec.execute(mFuture);
 
        return this;
    }   
}

说明:
1)外部传递参数params设置到WorkerRunnable中进行保管,在执行后台任务时,将此参数传递给doInBackground方法。
2)FutureTask(Callable<V> callable)构造方法中,将WorkerRunnable传递进去,保存在成员变量callable中。

SerialExecutor任务顺序分发

SerialExecutor负责将异步任务分发给ThreadPoolExecutor线程池,线程池执行完任务后再派发下个任务。

public abstract class AsyncTask<Params, Progress, Result> {
    ...
    private static class SerialExecutor implements Executor {
        // 定义队列,存储Runnable类型的成员
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;
        
        public synchronized void execute(final Runnable r){
            // 将mFuture封装到Runnable中,并存入队列mTasks的队尾
            mTasks.offer(new Runnable(){
                public void run(){
                    try{
                        r.run();
                    }finally{
                        scheduleNext();
                    }
                }
            });

            if(mActive == null){
                scheduleNext();
            }
        }
        protected synchronized void scheduleNext(){
            // 从mTasks的队首取一个任务
            if(mActive = mTasks.poll() != null){
                // 将取出的任务放入线程池中执行
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
}

说明:SerialExecutor将传入的mFuture封装到Runnable,使每次向线程池中传递一个任务,在执行完毕后再取下一个任务,达到顺序执行的目的。

线程任务的执行
public abstract class AsyncTask<Params, Progress, Result> { 
    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;

    /**
    *创建新的异步任务,这个构造器必须在UI线程上调用。
    */
    public AsyncTask() {
        this((Looper) null);
    }
 
    /**
     * 创建新的异步任务,这个构造器必须在UI线程上调用。
     * @hide---不可以直接调用
     */
    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
             ? getMainHandler(): new Handler(callbackLooper);
    
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
            //设置为true,代表当前方法被调用过
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    //设置线程的优先级
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    
                    //将任务参数传递给doInBackground方法处理
                    result = doInBackground(mParams);  
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    // 处理执行结果
                    postResult(result);
                }
                return result;
            }
        };
    
        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
        
        // 利用handler将结果发送到主线程
        private Result postResult(Result result) {
            @SuppressWarnings("unchecked")
            Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                                   new AsyncTaskResult<Result>(this, result));
            message.sendToTarget();
            return result;
        }
    }
}

1> 线程池执行FutureTask,调用其成员变量callable的call()方法,即WokerRunnable被执行。
2> WokerRunnable的call()方法中执行doInBackground(mParams),并把结果通过postResult(Result result) 传递给Hander,最终会调用重写的onPostExecute(result)方法。

参考资料

[1] Android应用性能优化,Herve Guihot
[2] AsyncTask使用及解析,(https://blog.csdn.net/qq_37321098/article/details/81625580)

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

推荐阅读更多精彩内容