首页 > 学院 > 开发设计 > 正文

详解AsyncTask实现原理

2019-11-07 23:21:08
字体:
来源:转载
供稿:网友

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 workprivate 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线程中)。

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

下面我们接着查看executeOnExecutor方法:
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表