AsyncTask源码分析

突然发现自己开发其实很少用到AsyncTask⁄(⁄ ⁄•⁄ω⁄•⁄ ⁄)在这里补上对它的源码分析

初见

在Android启蒙导师黑马程序员的视频中曾经听到过一句话,AsyncTask:轻量级的线程工具,方便快捷的实现工作线程到主线程的通信。
在翻看AsyncTask源码时一部分注释如下

/**
 * <p>AsyncTask enables proper and easy use of the UI thread. This class allows you
 * to perform background operations and publish results on the UI thread without
 * having to manipulate threads and/or handlers.</p>
 *
 * <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
 * and does not constitute a generic threading framework. AsyncTasks should ideally be
 * used for short operations (a few seconds at the most.) 

大意呢就是AsyncTask可以允许你在工作线程中发布结果到UI线程,但是它适合短时间操作最多几秒钟,嗨哟,这么傲娇,走了走了,还是滚去用RxJava算了,HandlerThread也行。
AsyncTask:别看注释吓人,我这样都没被淘汰,亲儿子啊~~
那我们再留下看看他还有什么话要说(;¬_¬)

使用

可以看到AsyncTask需要传入3个泛型参数
public abstract class AsyncTask<Params, Progress, Result>
依次代表传入工作线程的参数,进度回调,回调给UI线程的结果,艾玛,都有进度回调,这么一看好像很NB的样子。
我们随便写个AsyncTask看看

TestTask task = new TestTask();
task.execute(1, 2, 3, 4, 5, 6);
/**
     * 测试
     */
    class TestTask extends AsyncTask<Integer, Integer, String> {
        private static final String TAG = "TestTask";

        @Override
        protected void onPreExecute() {
            //预处理(UI线程)
        }

        @Override
        protected String doInBackground(Integer... params) {
            //必须实现,异步耗时任务(工作线程)
            StringBuilder sb = new StringBuilder();
            for (int i : params) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                sb.append(i);
            }
            Log.d(TAG, "doInBackground: " + sb.toString());
            return sb.toString();
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            //进度回调(UI线程)
            Log.d(TAG, "onProgressUpdate: " + values[0]);
        }

        @Override
        protected void onPostExecute(String s) {
            //回调给UI线程(UI线程)
            Log.d(TAG, "onPostExecute: " + s);
        }
    }

过了一会果然输出了2行日志,进度什么的还是要自己来搞
doInBackground: 123456
onPostExecute: 123456

分析

那么我们来看看execute这个方法是在怎么启动的

//UI线程限定
 @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
 @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != 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 = Status.RUNNING;

        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

看到这里,我们有了大概的思路,execute方法内部调用了默认的执行方法来执行,相对状态进行校验过后,将参数赋值后,执行方法,我们接下来先看看参数是在怎么赋值的,
mWorker.mParams = params;

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

可以发现mWorker其实就就是简简单单一个实现Callback接口的静态内部类,里面存放参数,那么exec.execute(mFuture);呢,execute传递进去了一个默认的执行规则

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
//execute 默认
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

其中Executor是一个接口

public interface Executor {
    void execute(Runnable command);
}

SerialExecutor,翻译出来叫做串行执行,我们来瞧瞧TA的实现

private static class SerialExecutor implements Executor {
        //双端队列,可以简单的理解成一个队列
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        //正在运行中的
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            //将一个runnable放置到队列末尾
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        //不考虑执行结果如何都会执行下一个任务
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                //第一次从这里开始执行任务
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            //从队列取出一个任务
            if ((mActive = mTasks.poll()) != null) {
                //真正开始执行
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

看到这里,敢情SerialExecutor就是THREAD_POOL_EXECUTOR的封装版,保证里面的runnable都是串行执行,名至实归,看到这里,笔者突然发现小小的AsyncTask中蕴含了不少的东西,那么我们完整的看下AsyncTask的构造器和成员变量

深入

//API25
public abstract class AsyncTask<Params, Progress, Result> {
    //手机CPU数目
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    //根据CPU来配置核心线程数目,每个编译版本可能不一样
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
    //线程池最大的容纳数量
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    //空线程30s保活
    private static final int KEEP_ALIVE_SECONDS = 30;
    //配置线程池时,给每个线程名字命名
    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };
    //阻塞队列,限长128
    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);
    //线程池的执行
    public static final Executor THREAD_POOL_EXECUTOR;
    //静态代码块,全局都是这个线程池
    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;
    }

    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    //消息的what
    private static final int MESSAGE_POST_RESULT = 0x1;
    private static final int MESSAGE_POST_PROGRESS = 0x2;

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    //回调的Handler
    private static InternalHandler sHandler;
    //实现Callback,存放参数
    private final WorkerRunnable<Params, Result> mWorker;
    //执行的具体方法
    private final FutureTask<Result> mFuture;
    //记录状态
    private volatile Status mStatus = Status.PENDING;
    //高比发下保证原子性操作,记录任务是否取消,是否执行
    private final AtomicBoolean mCancelled = new AtomicBoolean();
    private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

    private static class SerialExecutor implements Executor {
        //上文已有,省略。。。
    }

    public enum Status {
        //等待执行
        PENDING,
        //执行中
        RUNNING,
        //结束
        FINISHED,
    }


    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);
                    //noinspection unchecked
                    //执行方法
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    //结果post
                    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);
                }
            }
        };
    }

每处地方笔者几乎都打上了注释,毕竟我就是那么的贴心,恩(°∀°)ノ,其中有几处细节
AsyncTask的线程池配置根据手机配置来的,缓冲队列默认是128,也就是说如果有一只4核的手机,那么根据如上API25的配置

  • CPU_COUNT=4
  • CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)) = 4
  • MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1 = 7

线程池中最大容纳7个线程,同时处理4个工作线程,满出来的放到BlockingQueue中,也就是128的数量限制,默认还是串行执行,如果工作线程过于密集,不仅会阻塞后续的线程,还有可能抛出RejectedExecutionException异常,被线程池拒绝,至此,前文的AsyncTask的注释提示适合短时间的任务也就揭开了面纱。
当后台任务完成会调用这个方法返回结果

 private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }
private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());
        }

        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    // There is only one result
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }
private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }

通过InternalHandler来分发消息,然后对应执行mTask的相关回调方法

总结

综上所诉,AsyncTask驱动的其实是mFuture,本质也是一个Runnable;进度回调由我们自己调用publishProgress,工作线程限定

    @WorkerThread
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

一遍源码看下来,AsyncTask其实是将一个异步任务分配到线程池中来执行,完成后通过Handler来分发的一个封装的库,由于默认的执行还是串行的,虽然支持多线程并行,但是不太适合高频的网络请求,适用于一些轻量的IO操作。
Ps:由于使用过程中带有一些坑,就偷懒不放调用流程图了

参考:
AsyncTask和AsyncTaskCompat源码解析
Android AsyncTask 源码解析

凡劳苦担重担的人可以到我这里来,我就使你们得安息。 (马太福音 11:28 和合本)

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

推荐阅读更多精彩内容

  • 使用AsyncTask的一般步骤是: 定义一个类继承自AsyncTask,实现抽象方法 new 一个AsyncTa...
    yk_looper阅读 388评论 0 2
  • 前言 此前写过一篇AsyncTask源码分析的文章,但写的不是很好,最近看过了android7.0的AsyncTa...
    刘望舒阅读 1,788评论 3 15
  • 基础知识点 1.线程池Executor2.Future3.Callable4.中断线程5.Handler6.枚举7...
    Armstrong_Q阅读 849评论 3 19
  • 简介:AsyncTask是Android中用于异步操作的类。尽管现在已经逐渐被强调舍弃,不过就源码来说,Async...
    非墨Zero阅读 256评论 1 0
  • 少年锦时又回到春末的五月凌晨的集市人不多小孩在门前唱着歌阳光它照暖了溪河柳絮乘着大风吹树影下的人想睡沉默的人从此刻...
    芳华正茂阅读 225评论 0 0