AsyncTask阅读笔记

简介

AsyncTask是Android提供的一种方便的线程转换操作的简易框架,可以让使用者在UI线程调用,准备,然后在子线程中执行耗时操作,并且在执行过程中不停地通知主线程,最后子线程任务完成之后还可以将结果回调到UI线程。

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

其中Params是执行子线程任务的时候传入的参数,Progress是在子线程任务进行过程中通知UI线程的时候传的参数类型,Result是子线程任务结束之后传递给UI线程的结果。
以下分析基于Android的API25的源码:

参数

可以跳过,主要是用于参考

    //当前可用CPU核心数目
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    //核心线程数至少两个,最多4个,CPU_COUNT-1是为了节省一个核心用于其它工作
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    //线程池中最大线程数目2 * 可用CPU核心 + 1
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    //线程最大空闲时间,超过30s会被回收
    private static final int KEEP_ALIVE_SECONDS = 30;
    //最大容纳128个任务,如果还有任务进入,会阻塞等待有任务执行完毕再进入队列中
    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);
    //所有AsyncTask子线程任务实际执行的时候使用的线程池
    //不过这个在sDefaultExecutor的调度下默认是串行的,只不过是在当前线程池中执行
    public static final Executor THREAD_POOL_EXECUTOR;
    //主线程Handler
    private static InternalHandler sHandler;
    //主线程Handler的回调码
    //子线程任务完成,将Result回调到主线程
    private static final int MESSAGE_POST_RESULT = 0x1;
    //子线程任务进行中,尝试将Progress通知主线程的Handler
    private static final int MESSAGE_POST_PROGRESS = 0x2;
    //在子线程中进行任务的串行调度的一个线程池
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    //线程中的工作者,用于生产Result
    private final WorkerRunnable<Params, Result> mWorker;
    //线程池中的实际执行的计算任务,等待Result的计算完成
    private final FutureTask<Result> mFuture;
    //当前AsyncTask的执行状态
    private volatile AsyncTask.Status mStatus = AsyncTask.Status.PENDING;
    //当前AsyncTask是否取消,线程安全
    private final AtomicBoolean mCancelled = new AtomicBoolean();
    //当前子线程任务是否开始,线程安全
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
    /**
     * AsyncTask状态的枚举
     */
    public enum Status {
        //当前任务还没有开始
        PENDING,
        //当前任务执行中
        RUNNING,
        //当前任务完全执行结束,在主线程处理Result之后
        FINISHED,
    }

可以看到AsyncTask使用了状态模式,通过状态枚举来管理任务的进行流程。

初始参数

    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }

实际上就是通过之前的参数建立了一个线程池,这个线程池最少两个最多4个核心线程,最多可以容纳2 * 可用CPU核心 + 1个线程,其中核心线程可以被回收。线程的最大空闲时间为30s。
后面会结合SERIAL_EXECUTOR作进一步的分析

运行流程

    /**
     * 在主线程调用,开始执行任务
     * @param params 任务执行时候需要输入的参数
     */
    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        //默认通过SERIAL_EXECUTOR执行
        return executeOnExecutor(sDefaultExecutor, params);
    }

    /**
     * 在主线程中调用
     * 在制定的线程池中开始执行任务
     * @param exec 需要执行任务的线程池
     * @param params 执行参数
     */
    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
                                                                       Params... params) {
        //一个AsyncTask只能执行一次
        if (mStatus != AsyncTask.Status.PENDING) {//状态模式的一种应用
            switch (mStatus) {
                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 = AsyncTask.Status.RUNNING;//标记Task状态为运行中

        onPreExecute();//第一个回调方法

        mWorker.mParams = params;//记录当前任务的参数
        exec.execute(mFuture);//在线程池中执行任务

        return this;
    }

注意到一个AsyncTask只能执行一次,无法重复执行。
虽然AsyncTask的注解标记在主线程中执行,但是实际上执行在子线程也是可以的,不过这个时候onPreExecute()就是回调在子线程。简单说就是onPreExecute()执行在调用execute()的线程中。
AsyncTask可以通过executeOnExecutor执行在自定义的线程池中,不过这样就脱离AsyncTask的任务管理。这个根据自己的使用场景来定。
接着看mFuture任务:

    /**
     * 一个子线程中执行的Callable,具有计算结果Result
     * 内部持有执行参数
     */
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }

    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                //该方法是在线程池中的线程执行
                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 {
                    //会尝试将任务发送到主线程Handler中进行回到
                    postResult(result);
                }
                //保存计算结果,在FutureTask中可以通过get()获得
                return result;
            }
        };
        //mWorker是实际的计算任务
        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                //一般来说到这里mWorker的call()都是执行完成的
                //除非当任务被取消了,但是此时还没有开始call()
                //这个时候也要发送结果出去
                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
     * @param result 子线程任务的结果
     */
    private Result postResult(Result result) {
        //向主线程Handler发送任务执行完毕消息
        //结果封装在AsyncTaskResult中
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

    /**
     * 单例的主线程handler,这里可以理解为懒加载
     * 否则一般来说可能会通过static final直接建立Handler
     */
    private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

    /**
     * 如果当前子线程任务还没有开始就已经被取消的时候尝试发送结果到主线程handler
     */
    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

    /**
     * 取消当前任务
     * @param mayInterruptIfRunning true的话会尝试中断运行中的线程
     */
    public final boolean cancel(boolean mayInterruptIfRunning) {
        mCancelled.set(true);//标记任务被取消
        //尝试取消FutureTask的任务
        return mFuture.cancel(mayInterruptIfRunning);
    }

AsyncTask通过一个FutureTask在线程池中进行计算任务Callacble,并且等待计算结果。
如果任务被取消或者完成之后会通过主线程Handler发送消息进行后续的回调操作。

    /**
     * 主线程Handler,处理消息回调
     */
    private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());//主线程Looper,意味着handleMessage在主线程执行
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT://结束
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS://子线程执行中
                    result.mTask.onProgressUpdate(result.mData);//这里就是回调onProgressUpdate(Progress)方法
                    break;
            }
        }
    }

    /**
     * 处理任务完成
     * @param result
     */
    private void finish(Result result) {
        if (isCancelled()) {//当前任务已经被取消
            onCancelled(result);//回调onCancelled
        } else {
            onPostExecute(result);//回调onPostExecute
        }
        mStatus = AsyncTask.Status.FINISHED;//标记当前任务完成
    }

    /**
     * 最好在工作线程中调用,比方说doInBackground
     * @param values 要发送的值
     */
    @WorkerThread
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {//当前任务没有取消
            //将当前任务进度发送到主线程Handler中,并且进行回调
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

可以看到onProgressupdate、onPostExecute和onCancelled都是在UI线程中被调用,分别代表的是任务进行中、任务正常完成、任务被取消。
最后看一下调度的线程模型

    /**
     * 默认的执行任务的线程池
     */
    private static class SerialExecutor implements Executor {
        //实际上这个线程池是static的,意味着所有AsyncTask都是共用这个任务队列
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        //当前执行的任务
        Runnable mActive;

        /**
         * 该方法实际上是调用在execute的调用线程中的
         * 串行执行任务
         */
        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();//执行FutureTask任务
                    } finally {
                        scheduleNext();//尝试执行下一个任务
                    }
                }
            });
            //当前如果有任务执行,等待执行中的任务执行完成之后再尝试执行下一条任务
            if (mActive == null) {
                //当前没有任务在执行
                scheduleNext();//尝试执行下一条任务
            }
        }

        /**
         * 如果当前任务队列中还有任务,在子线程中执行下一条任务
         */
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {//尝试获取当前队列中下一条任务
                THREAD_POOL_EXECUTOR.execute(mActive);//在子线程执行任务
            }
            //没有任务直接结束,等待下一次调用
        }
    }

可以看到AsyncTask首先在execute的调用线程中开启任务,任务的执行条件必须满足之前的任务执行完成或者没有任务,即任务是串行执行的。
然后实际的任务是执行在THREAD_POOL_EXECUTOR线程池中的子线程,结合之前的参数可以稍微分析一下,当第一个任务开始,线程池会创建一个核心线程然后执行,这个时候如果有其它任务想要开始,则会进入任务队列中等待执行。
等上一次任务执行完成之后,将会执行下一个任务,此时如果超过上次执行任务完成30s之后,此时核心线程被回收,则会重新创建,否则在很快的时间内都会一直复用之前创建的核心线程,
所以说AsyncTask最好用于一些快速完成的任务。

总结

AsyncTask默认是串行执行任务的,允许执行在自定义的线程池中。
如果想要最大化利用线程池,最好保证AsyncTask的任务足够快。
每一个AsyncTask只能执行一次,不应许重复执行。
AsyncTask可以在子线程execute(),此时onPreExecute()会在子线程调用,官方还是推荐在UI线程中execute()。
onProgressUpdate一定执行在UI线程中,onCancelled和onPostExecute互斥且一定执行在UI线程中。

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

推荐阅读更多精彩内容

  • Android Handler机制系列文章整体内容如下: Android Handler机制1之ThreadAnd...
    隔壁老李头阅读 3,183评论 1 15
  • Android开发者:你真的会用AsyncTask吗? 导读.1 在Android应用开发中,我们需要时刻注意保证...
    cxm11阅读 2,701评论 0 29
  • Android中的线程 线程,在Android中是非常重要的,主线程处理UI界面,子线程处理耗时操作。如果在主线程...
    shenhuniurou阅读 748评论 0 3
  • 虽说现在做网络请求有了Volley全家桶和OkHttp这样好用的库,但是在处理其他后台任务以及与UI交互上,还是需...
    weishu阅读 3,362评论 10 55
  • 每天写育儿记录,就忽略了本应练习的写作! 今天工作还不错,沟通三个学员,前两个是老学员了,看到他们的成长由衷的开心...
    sanyaojing阅读 148评论 0 0