AsyncTask机制原理解析

AsyncTask机制原理解析

Android为我们提供了2种方便的异步处理方案,Handler和AsyncTask,两种方式适合的场景网上一搜就知道了,但是为什么呢?这篇分析将为你揭晓答案。前面分析了Handler的机制原理,还不熟悉的可以看下Handler机制原理。通过本篇的学习,来了解AsyncTask的工作原理。本篇内容较多,本着结论先行,分析在后的原则来看。

一,使用

使用呢是十分的简单,用一个类继承AsyncTask,拿着这个对象,执行execute就可以开始执行后台任务了。还是找个例子吧

* private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
*     protected Long doInBackground(URL... urls) {
*         int count = urls.length;
*         long totalSize = 0;
*         for (int i = 0; i < count; i++) {
*             totalSize += Downloader.downloadFile(urls[i]);
*             publishProgress((int) ((i / (float) count) * 100));
*             // Escape early if cancel() is called
*             if (isCancelled()) break;
*         }
*         return totalSize;
*     }
*
*     protected void onProgressUpdate(Integer... progress) {
*         setProgressPercent(progress[0]);
*     }
*
*     protected void onPostExecute(Long result) {
*         showDialog("Downloaded " + result + " bytes");
*     }
* }
*new DownloadFilesTask().execute(url1, url2, url3);

这个例子是AsyncTask源码中注释提到的例子。我们看到AsyncTask有3个泛型参数,分别代表什么意思呢

 *     <li><code>Params</code>, the type of the parameters sent to the task upon
 *     execution.</li>
 *     <li><code>Progress</code>, the type of the progress units published during
 *     the background computation.</li>
 *     <li><code>Result</code>, the type of the result of the background
 *     computation.</li>

简单来说,第一个参数(是个可变参数)是传给后台任务用的,第二个参数是后台任务执行过程中更新进度使用,第三个参数是后台任务执行完的返回结果。除此之外,还有几个方法介绍一下

 * <p>AsyncTask must be subclassed to be used. The subclass will override at least
 * one method ({@link #doInBackground}), and most often will override a
 * second one ({@link #onPostExecute}.)</p>

至少重写一个方法,doInBackground,这个方法用来执行后台任务的,运行在工作线程

    @WorkerThread
    protected abstract Result doInBackground(Params... params);

通常,我们还会重写一个onPostExecute,这个方法是将后台任务的结果post给UI线程,运行在UI线程

    @MainThread
    protected void onPostExecute(Result result) {
    }

其实,另外还有2个方法我们需要关注下,这里将4个方法的注释都贴出来

 * <p>When an asynchronous task is executed, the task goes through 4 steps:</p>
 * <ol>
 *     <li>{@link #onPreExecute()}, invoked on the UI thread before the task
 *     is executed. This step is normally used to setup the task, for instance by
 *     showing a progress bar in the user interface.</li>
 *     <li>{@link #doInBackground}, invoked on the background thread
 *     immediately after {@link #onPreExecute()} finishes executing. This step is used
 *     to perform background computation that can take a long time. The parameters
 *     of the asynchronous task are passed to this step. The result of the computation must
 *     be returned by this step and will be passed back to the last step. This step
 *     can also use {@link #publishProgress} to publish one or more units
 *     of progress. These values are published on the UI thread, in the
 *     {@link #onProgressUpdate} step.</li>
 *     <li>{@link #onProgressUpdate}, invoked on the UI thread after a
 *     call to {@link #publishProgress}. The timing of the execution is
 *     undefined. This method is used to display any form of progress in the user
 *     interface while the background computation is still executing. For instance,
 *     it can be used to animate a progress bar or show logs in a text field.</li>
 *     <li>{@link #onPostExecute}, invoked on the UI thread after the background
 *     computation finishes. The result of the background computation is passed to
 *     this step as a parameter.</li>
 * </ol>

onPreExecute,在任务开始前的准备工作,运行在UI线程,比如准备进度条

onProgressUpdate,一般用在后台任务进行时,更新进度等动作,运行在UI线程

二,原理分析

分析之前,我们先说结论,AsyncTask是对线程池和Handler的封装,任务只能执行一次,多次执行报错,任务可取消,默认串行执行。

2.1,成员分析

    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;
这里能看到,通过获取CPU的核数,来确定核心池数量和最大池数量,这个池是啥,应该就是线程池吧。
    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());
        }
    };

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

这里有个ThreadFactory,其实就是生产线程的工厂嘛,BlockingQueue,看名字是个阻塞的队列,其实就是放我们要执行的任务的。

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

先看Executor注释,可以用来执行并行任务。这里有个静态final的对象,看名字,没错,这就是线程池了。不过,这里不是说并行吗,别急,往下看

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) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

这里还有个串行的Executor,是啥呢,看到关键的一行,通过THREAD_POOL_EXECUTOR去execute,到这我们似乎应该明白了,pool是默认用来执行异步任务的,那么这个SerialExecutor其实可以理解为一个容器,对接收到的任务暂存,串行交给pool去处理。这也验证了AsyncTask默认是串行执行的,当然了,你可以自己定制为并行执行。

    private static final int MESSAGE_POST_RESULT = 0x1;
    private static final int MESSAGE_POST_PROGRESS = 0x2;

2种类型的消息,不需要解释了吧。

    /**
     * Indicates the current status of the task. Each status will be set only once
     * during the lifetime of a task.
     */
    public enum Status {
        /**
         * Indicates that the task has not been executed yet.
         */
        PENDING,
        /**
         * Indicates that the task is running.
         */
        RUNNING,
        /**
         * Indicates that {@link AsyncTask#onPostExecute} has finished.
         */
        FINISHED,
    }

用这个枚举来标识任务的状态,看注释,每个任务只能执行一次。

private static InternalHandler sHandler;
    private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }
    private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());
        }

到这里,知道了吧,AsyncTask内部有一个UI线程的Handler。

AsyncTask是可以取消任务的,其实是使用了FutureTask和Callable。 这里介绍一下,Callable是个借口,与Runnable类似,不同的是Runnable没有返回值,Callable可以指定返回值。而关于FutureTask,看一下源码注释吧

 * <p>A {@code FutureTask} can be used to wrap a {@link Callable} or
 * {@link Runnable} object.  Because {@code FutureTask} implements
 * {@code Runnable}, a {@code FutureTask} can be submitted to an
 * {@link Executor} for execution.

意思是说,FutureTask可以封装Callable和Runnable,并且可以让Executor执行起来。FutureTask有两个关键方法,done,表示任务执行完,cacel,表示任务取消。AsyncTask之所以能取消,其实就是通过FutureTask来取消的

    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;
    private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }
mWorker是实现了Callable的对象。看到这里,应该对AsyncTask的特点有了初步的认识,没关系,下面还会继续加深。

2.2,工作原理分析

先看一下AsyncTask的构造方法

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

这里初始化了2个成员,mWorker和mFuture,内部的方法并不会执行。那么就看看execute方法吧

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

这里说明了,方法运行在UI线程。

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

一上来,先对status进行判断和初始化(这里是一个任务只能执行一次的实现),接下来,看到了onPreExecute,就是我们可能需要重写的方法。然后把params赋给了mWorker。exec.execute就是sDefaultExecutor执行execute。又因为

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

所以也就是SERIAL_EXECUTOR的execute了。

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

这里我们前面分析过,是用了个容器,让任务串行执行。传给pool的是mActive,其实是对mFuture的封装,而mFuture初始化又是使用的mWorker,所以最终执行到了mWorker的call方法,也就构造方法中的第一个成员的方法。这里会执行doInbackground,这里是在工作线程执行的。执行完会得到一个result,在finally中post出去。

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

Handler前面已经介绍过了,这里知道封装完Message,发送给UI线程即可。AsyncTaskResult是一个内部类

    private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

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

看一下handleMessage

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

可以看到,POST_RESULT会调用finish方法

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

这里我们看到了熟悉的onPostExecute方法,参数是result。那么POST_PROGRESS是怎么处理的呢?我们知道,doInBackground可能需要较长的时间执行完,中间的过程想更新进度条怎么办呢?可以在doInbackground中调用publishProgress

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

如果任务没被取消,则通过Handler发送出去,那么接下来又到handleMessage了,看一下前面的代码,onProgressUpdate

    @MainThread
    protected void onProgressUpdate(Progress... values) {
    }
看到这里,工作原理分析就结束了,我们已经能知道AsyncTask是对线程池和Handler的封装,任务只能执行一次,任务可以取消,默认串行执行(注意理解为什么有这些特点)


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值