首页 > 代码库 > AsyncTask源码解析

AsyncTask源码解析

本文分为以下几部分:
1.AsyncTask的使用介绍
2.AsyncTask的实现逻辑
3.其它牵涉到的概念
 
1.AsyncTask的使用
技术分享
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {    @Override    protected void onPreExecute() {        progressDialog.show();    }    @Override    protected Boolean doInBackground(Void... params) {        try {            while (true) {                int downloadPercent = doDownload();                publishProgress(downloadPercent);                if (downloadPercent >= 100) {                    break;                }            }        } catch (Exception e) {            return false;        }        return true;    }    @Override    protected void onProgressUpdate(Integer... values) {        progressDialog.setMessage("当前下载进度:" + values[0] + "%");    }    @Override    protected void onPostExecute(Boolean result) {        progressDialog.dismiss();        if (result) {            Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();        } else {            Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();        }    }}
View Code
技术分享
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {    ……}
View Code

这里我们把AsyncTask的第一个泛型参数指定为Void(doInBackground(Params...)),表示在执行AsyncTask的时候不需要传入参数给后台任务。第二个泛型参数指定为Integer,表示使用整型数据来作为进度显示单位(onProgressUpdate(Progress...))。第三个泛型参数指定为Boolean(onPostExecute(Result)),则表示使用布尔型数据来反馈执行结果。

 

1. onPreExecute()
这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
2. doInBackground(Params...)---只有在这个方法中执行耗时操作
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。
3. onProgressUpdate(Progress...)
当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
4. onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
 
执行:执行如下代码,就开启一个下载任务(实际可能只是放到队列中,没有开始执行)
技术分享
new DownloadTask().execute();
View Code
需要传递参数给后台任务,那么如何定义?
这样定义:
技术分享
class DownloadTask extends AsyncTask<String, Integer, Boolean>{@Override    protected Boolean doInBackground(String... params) {    ....// String url = params[0]; ,这个就是客户端传入的参数// String url2 = params[1]; 客户端传入的参数   }}//使用DownloadTask  task = new DownloadTask();task.execute("url","url2");
View Code

 

2.AsyncTask的实现逻辑

1).AsyncTask是一个抽象类,子类继承抽象类,要实现抽象类的抽象方法
2).要实现的抽象方法:
抽象类AsyncTask只有一个抽象方法doInBackground,子类是必须要实现的
技术分享
    /**     * Override this method to perform a computation on a background thread. The     * specified parameters are the parameters passed to {@link #execute}     * by the caller of this task.     *     * This method can call {@link #publishProgress} to publish updates     * on the UI thread.     *     * @param params The parameters of the task.     *     * @return A result, defined by the subclass of this task.     *     * @see #onPreExecute()     * @see #onPostExecute     * @see #publishProgress     */    @WorkerThread    protected abstract Result doInBackground(Params... params);
View Code
3).创建子类的一个实例,然后执行execute方法,这样就开启一个AsyncTask任务。
那么,这个时候,要查看创建一个实例,然后执行execute方法,都做了什么。
实例化AsyncTask时做的事情:
技术分享
  /**     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.     */    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) {            @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);                }            }        };    }   private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {        Params[] mParams;    }
View Code

调用execute方法做的事情:

技术分享
  /**     * Executes the task with the specified parameters. The task returns     * itself (this) so that the caller can keep a reference to it.     *     * <p>Note: this function schedules the task on a queue for a single background     * thread or pool of threads depending on the platform version.  When first     * introduced, AsyncTasks were executed serially on a single background thread.     * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed     * to a pool of threads allowing multiple tasks to operate in parallel. Starting     * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being     * executed on a single thread to avoid common application errors caused     * by parallel execution.  If you truly want parallel execution, you can use     * the {@link #executeOnExecutor} version of this method     * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings     * on its use.     *     * <p>This method must be invoked on the UI thread.     *     * @param params The parameters of the task.     *     * @return This instance of AsyncTask.     *     * @throws IllegalStateException If {@link #getStatus()} returns either     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.     *     * @see #executeOnExecutor(java.util.concurrent.Executor, Object[])     * @see #execute(Runnable)     */    @MainThread    public final AsyncTask<Params, Progress, Result> execute(Params... params) {        return executeOnExecutor(sDefaultExecutor, params);    }    /**     * An {@link Executor} that can be used to execute tasks in parallel.     */    public static final Executor THREAD_POOL_EXECUTOR            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
View Code
似乎,阅读完注释,就知道它的执行原理了。
 
执行execute,就是创建一个任务,把这个任务加入到一个队列中,然后等待线程(可以是单一的线程,也可以是从线程池中取出来的线程)来执行。因为,所有的实例都是使用同一个THREAD_POOL_EXECUTOR(是一个静态final变量,当某个类首次加载的时候,就会初始化,之后就是供所有的实例使用)。那么,每当我们new DownloadTask().execute("url"),创建一个任务,然后将任务推给队列,这个执行的顺序是怎样的呢?
在早期的系统版本(低于android.os.Build.VEERSION_CODES#DONUT的版本),是采用串行的方式执行,某一个时刻只有一个任务在执行;从系统版本android.os.Build.VERSION_CODES#DONUT开始,修改为并行的方式执行,多个任务,分配给多个线程,并行执行;到了版本android.os.Build.VERSION_CODES#HONEYCOMB,它又修改为串行的方式。真是折腾。
并行执行的问题,看应用场景,如果某些任务是要按顺序一个一个执行的,对执行顺序有要求的,那么这些任务就不能采用并行的方式执行,而要采用串行的方式执行。因为并行执行,它不能确保所有任务的执行顺序。
 
现在默认是串行的方式,如果需要该任务是并行执行的,那么,使用如下方式:
技术分享
DownloadTask task = new DownloadTask();task.executeOnExecutor(THREAD_POOL_EXECUTOR,"url");
View Code

 

4)executeOnExecutor(Executor exec,Params... params)执行细节

技术分享
  private final FutureTask<Result> mFuture;    /**     * An {@link Executor} that can be used to execute tasks in parallel.     */    public static final Executor THREAD_POOL_EXECUTOR            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);   /**     * 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 volatile Executor sDefaultExecutor = SERIAL_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);            }        }    }   /**     * Executes the task with the specified parameters. The task returns     * itself (this) so that the caller can keep a reference to it.     *     * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to     * allow multiple tasks to run in parallel on a pool of threads managed by     * AsyncTask, however you can also use your own {@link Executor} for custom     * behavior.     *     * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from     * a thread pool is generally <em>not</em> what one wants, because the order     * of their operation is not defined.  For example, if these tasks are used     * to modify any state in common (such as writing a file due to a button click),     * there are no guarantees on the order of the modifications.     * Without careful work it is possible in rare cases for the newer version     * of the data to be over-written by an older one, leading to obscure data     * loss and stability issues.  Such changes are best     * executed in serial; to guarantee such work is serialized regardless of     * platform version you can use this function with {@link #SERIAL_EXECUTOR}.     *     * <p>This method must be invoked on the UI thread.     *     * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a     *              convenient process-wide thread pool for tasks that are loosely coupled.     * @param params The parameters of the task.     *     * @return This instance of AsyncTask.     *     * @throws IllegalStateException If {@link #getStatus()} returns either     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.     *     * @see #execute(Object[])     */    @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;    }
View Code
a.SerialExecutor的实现逻辑:
1).mTasks.offer(Runnable),创建一个新的Runnable,添加到队列尾部。这个新的Runnable就是交给THREAD_POOL_EXECUTOR执行的任务。新的Runnable包含了从客户端提交的任务。
新的Runnable的逻辑是:先执行客户端提交的任务(也就是mFuture);然后,执行scheduleNext方法,scheduleNext做的事情,从队列的头部取出创建的Runnable,然后交给THREAD_POOL_EXECUTOR执行。
通过这种方式,就可以保证所有的任务是按照串行的方式来执行的。新添加的mFuture,使用新创建的一个Runnable,然后,将这个Runnable添加到队列尾部;然后,每次执行的时候,是从队列的的头部取添加的Runnable。
2).触发执行的入口是:
   if (mActive == null) {
         scheduleNext();
 }
3).scheduleNext方法,是同步方法,确保每次只有一个线程可以执行该方法。THREAD_POOL_EXECUTOR有多个线程。
技术分享
  protected synchronized void scheduleNext() {            if ((mActive = mTasks.poll()) != null) {                THREAD_POOL_EXECUTOR.execute(mActive);            }        
View Code

通过SerialExecutor,就可以实现串行的执行。

 

实现逻辑总结:
1.执行从在executeOnExecutor开始,在该方法中调用 onPreExecute(),这是在UI线程执行的。
2.当THREAD_POOL_EXECUTOR有空闲线程时,就会最终执行mFuture任务,而mFuture包含mWorker,它会执行:Result result = doInBackground(mParams);----mFuture是在第三方线程中运行的
3.将执行的结果发送给UI线程:
 在mWorker中会调用postResult方法;postResult方法,通过UI线程的handler发送消息。这里的关键是InternalHandler的创建,它使用的是Ui线程的Looper,这样消息才会给Ui线程的消息循环处理。
技术分享
  private Result postResult(Result result) {        @SuppressWarnings("unchecked")        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,                new AsyncTaskResult<Result>(this, result));        message.sendToTarget();        return result;    }    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());        }        @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;            }        }    }
View Code
4.执行过程中的进度更新:
因为该方法是在第三方线程中运行的,所以,同样是使用Ui线程的Handler来发送消息
技术分享
/**     * This method can be invoked from {@link #doInBackground} to     * publish updates on the UI thread while the background computation is     * still running. Each call to this method will trigger the execution of     * {@link #onProgressUpdate} on the UI thread.     *     * {@link #onProgressUpdate} will not be called if the task has been     * canceled.     *     * @param values The progress values to update the UI with.     *     * @see #onProgressUpdate     * @see #doInBackground     */    @WorkerThread    protected final void publishProgress(Progress... values) {        if (!isCancelled()) {            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();        }    }
View Code

 

 

3.其它牵涉到的概念介绍
 
1.FutureTask
2.Callable
 
 
 
引用:
部分示例代码引用自网络
 
 

AsyncTask源码解析