详解AsyncTask实现原理

AsyncTask允许执行后台计算并将计算结果发送给UI线程,过程中无需显示地借助Handler等。这是因为AsyncTask内部封装了线程池用于执行后台计算,并通过Handler将结果传递给UI线程。

根据下面AsyncTask的定义可知,主要包含三个参数,分别表示:参数类型、后台任务执行的进度类型、返回的结果类型。若不需要某个参数,可直接设为Void类型。

public abstract class AsyncTask<Params, Progress, Result> 
AsyncTask的使用步骤如下:

1)定义一个AsyncTask的子类;

子类中需要实现以下几个函数:

  • onPreExecute():执行在UI线程中,execute(Params... params)被调用后立即执行,主要用来在执行后台任务前对UI作一些标记,如启动一个进度条等。
  • doInBackGround(Params... params):在onPreExecute()执行完成后立即执行,用于执行后台任务,在该方法中可调用publishProgress(Progress... progress)来更新进度信息。
  • onProgressUpdate(Progress... progress):执行在UI线程中,publishProgress方法执行后将触发本方法执行,用以将进度信息更新到UI组件上。
  • onPostExecute(Result result):执行在UI线程中。当后台任务执行完成后,将触发被方法的调用,接收doInBackground方法返回的计算结果。

2)在UI线程中创建AsyncTask子类的实例;

3)在UI线程中调用AsyncTask子类实例的execute(Params... params)方法

注意,一个异步任务的实例只能执行一次,否则会抛出异常。这是因为异步任务执行时会检查其状态,若为已执行则抛出异常,我们会在下面的源码讲解中指出。

我们通过调用execute(Params... params)来开启异步任务,那么这个方法调用背后的原理是什么呢?接下来我们通过学习AsyncTask的源码来探讨AsyncTask的实现原理。


根据源码可知,execute方法仅仅是调用了executeOnExecutor方法。我们接着查看executeOnExecutor方法,可以看出该方法中首先对异步任务的状态进行检查,若状态为RUNNING、FINISHED则抛出相应的异常,否则设当前异步任务的状态为RUNNING,并调用onPreExecute方法对UI做一些标记,然后将execute方法传入的参数传递给mWorker,并通过线程池sDefaultExecutor执行任务。我们确实可以看出,execute和onPreExecute方法都是执行在UI线程中的。


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


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)");
        }
    }//执行前会判断AsyncTask的状态,这也是AsyncTask实例只能执行一次的原因。


    mStatus = Status.RUNNING;//将AsyncTask的状态置为正在运行

    onPreExecute();//调用此函数,在UI线程中对UI做一些标记,如开启一个进度条等

    mWorker.mParams = params;
    exec.execute(mFuture);//!!!线程池exec执行任务!!!execute函数传递过来的形参exec是线程池sDefaultExecutor


    return this;
}

exec.execute(mFuture)究竟做了什么呢?我们继续查看sDefaultExecutor和参数mFuture。从下面源码可以看出,sDefaultExecutor是SerialExecutor的实例,而SerialExecutor实现了Executor接口,是一个顺序执行任务的线程池,其主要作用是管理提交的任务,最终将任务交给THREAD_POOL_THREAD线程池执行。mFuture是FutureTask类型实例。

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

/**
 * An {@link Executor} that executes tasks one at a time in serial
 * order.  This serialization is global to a particular process.
 */
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();//一个顺序执行任务的线程池

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) {//取出双端队列mTasks的队头元素,赋值给mActive
            THREAD_POOL_EXECUTOR.execute(mActive);//!!!最终执行任务的是THREAD_POOL_EXECUTOR线程池!!!
        }
    }
}

THREAD_POOL_EXECUTOR是一个ThereadPoolThread类型的静态不可变实例,根据下面的源码可知,其核心线程数最小为2,最大为4,最大线程数为(CPU内核数*2+1),任务队列基于LinkedBlockingQueue实现,最大容量为128。

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
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;
private static final int KEEP_ALIVE_SECONDS = 30;

/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 */
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;
}

mWorker和mFuture都是在AsyncTask的构造函数中实例化的。mWorker是WorkerRunnable类型的实例,WorkerRunnable实现了Callable接口,并定义了一个Params[] params来接受AsyncTask的execute(Params... params)传入的参数。mWorker的实例化过程中调用了doInBackground函数,并将任务的执行结果传递给postResult(Result result)方法,并执行postResult方法。mWorker封装了后台任务,mFuture根据mWorker创建FutureTask实例,mFuture运行时将执行mWorker的call方法。


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

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

    mFuture = new FutureTask<Result>(mWorker) {//提交到线程池的任务,根据传入的mWorer创建实例,将在运行时执行mWorker的call()方法
        @Override
        protected void done() {
            try {
                postResultIfNotInvoked(get());//确保postResult一定会被调用。若任务未执行,则该方法将调用postResult
            } 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);
            }
        }
    };
}


private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();//任务是否执行的标志变量
    if (!wasTaskInvoked) {
        postResult(result);//若任务未执行,mWorker中的postResult就不会被调用,因此需主动调用通知UI线程
    }
}

private Result postResult(Result result) {//将任务的执行结果发送给handler,通过handler将执行结果发送给UI线程
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));//getHandler()返回sHandler(InternalHandler的实例)
    message.sendToTarget();
    return result;
}

根据上述postResult的源码可以看出,它的作用是将当前AsyncTask实例和任务的执行结果封装在一个AsyncTaskResult类型对象中,并通过InternalHandler类型的sHandler传递给UI线程。InternalHandler用以处理异步任务的线程池传递过来的任务的执行消息,包括执行进度和执行结果。可以看出,若消息是执行进度,则调用当前AsyncTask实例的onPrograssUpdate方法更新UI线程中的进度信息显示;若消息是执行结果,则调用当前AsyncTask实例的finish方法。

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> {//包装了当前AsyncTask实例和任务执行结果
    final AsyncTask mTask;
    final Data[] mData;

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

那么AsyncTask的finish究竟做了什么呢?从源码可以看出,在finish方法中对任务执行结果的返回进行类型判断,若是取消任务并返回则调用onCancelled方法;若是任务执行完成并返回则调用onPostExecute方法(可以看出执行在UI线程中)。最后,finish方法将异步任务的状态置为FINISHED。


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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值