Android AsyncTask 浅析(源代码取自 API level 23)

前言:本文章假定读者熟悉AsyncTask的基本用法。

下面,我们从构造一个AsyncTask开始讲起。构造函数中,它将初始化mWorkermFuture,而mWorker调用doInBackground完成实际的工作。

Created with Raphaël 2.1.0 client client AsyncTask() AsyncTask() execute() execute() new AsyncTask() mWorker = new WorkerRunnable() mFuture = new FutureTask(mWorker) executor.execute(mFuture);
mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);

        Process.setThreadPriority(
                Process.THREAD_PRIORITY_BACKGROUND);
        Result result = doInBackground(mParams);
        Binder.flushPendingCommands();
        return postResult(result);
    }
};

执行成功的情况下,postResult(result)最后会调用onPostExecute(),将结果发布至主线程。 同时,该return语句也将结果存放至 mFuture中。
此外需要注意的一点是,这里将线程优先级设置为了background,以防止占用过多的资源。

至于mFuture,主要用于执行任务的取消策略。具体的,可以看看AsyncTask.cancel()方法

public final boolean cancel(boolean mayInterruptIfRunning) {
    mCancelled.set(true);
    return mFuture.cancel(mayInterruptIfRunning);
}

此外,AsyncTask.get()也通过mFuture,将结果返回给调用者。




Created with Raphaël 2.1.0 execute() execute() executeOnExecutor() executeOnExecutor() executeOnExecutor(sDefaultExecutor, params)

默认情况下,讲在sDefaultExecutor中执行对应的任务,可以直接调用executeOnExecutorsetDefaultExecutor(executor)方法,选择一种执行策略。

AsyncTask提供两种Executor的实现,分别是AsyncTask.THREAD_POOL_EXECUTORAsyncTask.SERIAL_EXECUTORsDefaultExecutor默认情况下为SERIAL_EXECUTOR。此外,由于这两个Executor均是静态变量,整个应用共享一个线程池。

private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks =
            new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        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);
        }
    }
}

THREAD_POOL_EXECUTOR直接使用ThreadPoolExecutor实现,而SERIAL_EXECUTOR实际上也将工作交由THREAD_POOL_EXECUTOR处理,只是在内部使用一个ArrayDeque,以实现串行执行的功能。
此处需注意的,ArrayDeque.poll()方法并不阻塞,失败时,它将返回null,此时,也释放了mActive所指向(refer to)的对象,防止了内存的泄露。



Created with Raphaël 2.1.0 publishProgress() publishProgress() InternalHandler InternalHandler postResult() postResult() progress result

doInBackground()方法中,我们可以调用publishProgress()将进度发布至主线程。进度(grogress)和结果(result)的发布,都通过内部的InternalHandler类处理。

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

这里调用Looper.getMainLooper()以获取主线程的looper,从而确保结果发布至主线程。



此外,察看文档,还可以发现一个经常被忽略的函数:

// AsyncTask
public static void execute(Runnable runnable) {
    sDefaultExecutor.execute(runnable);
}

这里,我们可以将AsyncTask当成一个方便的线程池,偶尔用来执行一些Runnable(由于内部的线程池是公用的,不建议大量使用)。

还有一个我们直接查看源码可能无法发现的问题是,API level 11前,sDefaultExecutor 并非串行的。直接调用此方法,存在移植性问题。为此,我们可以使用support.v4包中的AsyncTaskCompat

public static <Params, Progress, Result>
AsyncTask<Params, Progress, Result> executeParallel(
        AsyncTask<Params, Progress, Result> task,
        Params... params) {
    if (task == null) {
        throw new IllegalArgumentException("task can not be null");
    }

    if (Build.VERSION.SDK_INT >= 11) {
        // From API 11 onwards, we need to manually select
        // the THREAD_POOL_EXECUTOR
        AsyncTaskCompatHoneycomb.executeParallel(task, params);
    } else {
        // Before API 11, all tasks were run in parallel
        task.execute(params);
    }

    return task;
}

AsyncTaskCompat只有一个静态方法executeParallel(),使用它,我们可以可移植地执行一个Runnable

基于此,如果应用需要明确的串行或并行执行,也应该显示调用executeOnExecutor()。由于该线程池是全局的,以此目的调用setDefaultExecutor并不是什么好主意。


另一点需要注意的是,THREAD_POOL_EXECUTOR的队列容量为128:

private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);




总结:AsyncTask总体上使用模板方法,将任务、取消、完成动作都交由子类实现,而执行策略则可以选择预定义的THREAD_POOL_EXECUTORSERIAL_EXECUTOR,也可以使用自己实现的Executor或自定义ThreadPoolExecutor。所以,执行策略还是非常灵活的。唯一的不足的没有(执行)失败策略,当然, 这也使得API更加的简单易用。

另外,在Executor中异步执行任务,而后通过Handler同步至主线程,也是Android平台的一个idiom,所谓的 half-sync, half-async。

文章中忽略了一些执行状态的细节,建议读者自己看看源代码,更细致地了解一下。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值