从0开始学安卓源码分析--AsyncTask源码

问题:

  1. 为什么一个AsyncTask只能被执行一次?
  2. 为什么AsyncTask是串行执行的, 不能并行吗?
  3. 为什么AsyncTask必须在UI线程中创建?

带着问题看源码, 稳.

看一个源码当然从其构造函数出发.

public AsyncTask(@Nullable Looper callbackLooper) {
        // 这里是为什么需要在主线程创建的原因, 不然怎么去更新ui?
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
        // WorkerRunnable实现callable接口并实现了它的call()方法
        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 {
                    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);
                }
            }
        };
    }

在这个构造方法里可以看到doInBackground(mParams) 方法, 也就是我们继承AsyncTask 复写的方法. 该方法返回了result结果, 最终在finally里调用postResult(result); 将结果通过handler传递进行处理. 在handler的handleMessage中会调用AsyncTask的finish()方法.

private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }

这里就找到了 onPostExecute(result); 将结果通过onPostExecute回调到上层了.

FutureTask是一个可管理的任务类, 它实现了Runnable和Callable两个接口, 稍微瞄一眼源码可知其可以对Runnable和Callable进行包装.
这里将WorkerRunnable作为入参传递给了FutureTask.
AsyncTask还并未开始执行, 真正执行需要调用AsyncTask.execute(params);, 并将params传递进去.

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

接着看executeOnExecutor(sDefaultExecutor, params);

这里需要看下AsyncTask的三个状态:

  • PENDING // 等待, 表示任务未执行
  • RUNNING // 表示任务正在运行
  • FINISHED // 表示任务已经完成结束
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        // 这里也是为什么一个AsyncTask只能执行一次的原因
        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()方法, 我们会在onPreExecute进行一些不耗时初始化的操作
        onPreExecute();

        mWorker.mParams = params;
        // 这里开始真正执行task任务
        exec.execute(mFuture);

        return this;
    }

调用exec.execute(mFuture);真正执行任务的exec是什么呢?
是传递进来的sDefaultExecutor .

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

private static class SerialExecutor implements Executor {
        // ArrayDeque 先进先出的双端队列
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            // 先将任务都存入队列里
            // 当前没有执行的任务则从队列中取出一个用THREAD_POOL_EXECUTOR执行.
            // 当前有执行的任务则是在任务完成后的finally里取出一个执行
            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);
            }
        }
    }

sDefaultExecutor是一个静态的串行执行器, 也就是如果都调用AsyncTask.execute(params);方法, 那么一个进程内所有AsyncTask都是由sDefaultExecutor 串行执行的. 这样保证一个时间只有一个任务执行.
如果需要并行执行任务则需要调用executeOnExecutor(Executor exec,Params... params)

另外注意再看看THREAD_POOL_EXECUTOR

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;
    }

线程池用到了一个容量128的阻塞队列.

到这里我们继承AsyncTask需要复写的方法,就都已经在流程中出现了

  • onPreExecute()
  • doInBackground(params)
  • onPostExecute(result)

还有另外一个onProgressUpdate(progress) 在哪儿呢?
搜索一下源码.

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

可以在doInBackground()中调用publishProgress()方法来进行进度的更新. 通过handler来将更新进度发布到UI线程并回调onProgressUpdate(progress)方法

AsyncTask的源码到这里已经分析完了, 总的来说难度不大. 最后AsyncTask可能会导致内存泄漏, 记得适时的调用AsyncTask的cancel()方法

理解记忆关键词:
handler、FutureTask(Runnable)、SerialExecutor线程池、主线程初始化

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

推荐阅读更多精彩内容

  • 架空小言,纯属虚构 chapter31 留家(2) 绍珩在酒店外廊的台阶上等了不到十分钟,匡家的车子便到了。他见匡...
    春衫冷阅读 5,785评论 3 3
  • 2017年3月,教育部公布最新的中国公民出国留学和外国公民留学中国情况。单从总量来看,和2016年相比,两项数据都...
    劈柴捌哥阅读 481评论 0 1
  • 《旋风女队》这部电影主要讲述了从小就拥有足球梦的女主人公吴小丽,因为身高不够的缘故,无法做职业球员后,一心想把足...
    左佳璐阅读 1,085评论 0 1