Android笔记(十三)AsyncTask源码分析

AsyncTask,异步任务,参照源码中的一段注解:

/**
 * <p>AsyncTask enables proper and easy use of the UI thread. This class allows you
 * to perform background operations and publish results on the UI thread without
 * having to manipulate threads and/or handlers.</p>
 *  <p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}
 * and does not constitute a generic threading framework. AsyncTasks should ideally be
 * used for short operations (a few seconds at the most.) If you need to keep threads
 * running for long periods of time, it is highly recommended you use the various APIs
 * provided by the <code>java.util.concurrent</code> package such as {@link Executor},
 * {@link ThreadPoolExecutor} and {@link FutureTask}.</p>
 * <p>An asynchronous task is defined by a computation that runs on a background thread and
 * whose result is published on the UI thread. An asynchronous task is defined by 3 generic
 * types, called <code>Params</code>, <code>Progress</code> and <code>Result</code>,
 * and 4 steps, called <code>onPreExecute</code>, <code>doInBackground</code>,
 * <code>onProgressUpdate</code> and <code>onPostExecute</code>.</p>
 * **/

从注解中我们大概可以知道AsyncTask的几个特点:

  • 更优雅、方便地使用UI线程
  • 适用于短耗时操作,如果你打算让子线程工作较长时间,建议使用线程池如Executor、ThreadPoolExecutor、FutureTask这些APIs
  • task执行时的三个参数:Params、Progress、Result,都能为void类型
  • task执行的四个方法:onPreExecute()、doInBackground()(must)、onProgressUpdate()、onPostExecute()(should)
* <h2>Cancelling a task</h2>
* <p>A task can be cancelled at any time by invoking {@link #cancel(boolean)}. Invoking
* this method will cause subsequent calls to {@link #isCancelled()} to return true.
* After invoking this method, {@link #onCancelled(Object)}, instead of
* {@link #onPostExecute(Object)} will be invoked after {@link #doInBackground(Object[])}
* returns. To ensure that a task is cancelled as quickly as possible, you should always
* check the return value of {@link #isCancelled()} periodically from
* {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)</p>
  • 从注解中,我们可以知道AsyncTask是如何cancel掉当前正在进行的任务的。调用cancel()方法可以将当前任务停止下来。调用该方法后,会导致isCancelled() return true;在我们线程在后台运行时,doInBackground()会一直在后台运行直到任务正常结束。在doInBackground中会调用isCancelled()来检查任务是否被取消。如果被取消,就会走到onCancelled()方法中;否则会一直执行,知道结束后调用onPostExecute()方法。
<h2>Threading rules</h2>
 * <p>There are a few threading rules that must be followed for this class to
 * work properly:</p>
 * <ul>
 *     <li>The AsyncTask class must be loaded on the UI thread. This is done
 *     automatically as of {@link android.os.Build.VERSION_CODES#JELLY_BEAN}.</li>
 *     <li>The task instance must be created on the UI thread.</li>
 *     <li>{@link #execute} must be invoked on the UI thread.</li>
 *     <li>Do not call {@link #onPreExecute()}, {@link #onPostExecute},
 *     {@link #doInBackground}, {@link #onProgressUpdate} manually.</li>
 *     <li>The task can be executed only once (an exception will be thrown if
 *     a second execution is attempted.)</li>
 * </ul>
  • task任务必须在主线程中实例化
  • execute()方法必须在主线程中执行
  • 不要手动调用几个回调方法
  • task只会被执行一次,否则会抛出异常
 * <h2>Memory observability</h2>
 * <p>AsyncTask guarantees that all callback calls are synchronized in such a way that the following
 * operations are safe without explicit synchronizations.</p>
 * <ul>
 *     <li>Set member fields in the constructor or {@link #onPreExecute}, and refer to them
 *     in {@link #doInBackground}.
 *     <li>Set member fields in {@link #doInBackground}, and refer to them in
 *     {@link #onProgressUpdate} and {@link #onPostExecute}.
 * </ul>
  • 内存问题,asynctask不用担心同步问题,回调方法都是同步方法,有严格意义上的先后顺序
<h2>Order of execution</h2>
 * <p>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 with
 * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are executed on a single
 * thread to avoid common application errors caused by parallel execution.</p>
 * <p>If you truly want parallel execution, you can invoke
 * {@link #executeOnExecutor(java.util.concurrent.Executor, Object[])} with
 * {@link #THREAD_POOL_EXECUTOR}.</p>
  • 最早以前,AysncTasks都是串行执行的,共用同一个background Thread。从Android 1.6开始,虚拟机中允许多个AsyncTasks并行执行。但是到了3.0时,由于并行运算遇到了一些问题,又将运行方式改成了串行计算,共用一个Bg Thread。如果你自己对处理线程并发很有信心,可以通过调用executeOnExecutor(THREAD_POOL_EXECUTOR)来并行运行该任务。

下面将介绍整个AysncTask代码的整体执行流程,让我们清楚地知道这个类是如何开启第一个子线程执行任务,如何不断地调用doInBackgound(),又是如何中断任务的。
执行AsyncTask的代码灰常简单,只消一句:

new DownloadFilesTask().execute(url1, url2, url3);

其中DownloadFileTask是我们自定义的任务。在这个execute()方法中,我们逐步找到工作子线程开始执行的地方。从代码中可以看出,execute()方法通过层层封装,最终在executeOnExecutor()方法中开始执行子线程

    @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)");
            }
        }
		//将AsyncTask任务设置为正在运行
        mStatus = Status.RUNNING;
		//执行onPreExecute()
        onPreExecute();
	    //将构造方法中的变量传递给mWorker这个Runnable
        mWorker.mParams = params;
        exec.execute(mFuture);//开始执行mFuture这个任务
        return this;
    }

默认情况下,上面的exec就是我们的SerialExecutor线程池,里面在真正执行线程任务

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

        public synchronized void execute(final Runnable r) {
            //offer方法表示向任务缓存队列中添加一个任务,mTasks代表了SerialExecutor这个串行线程池的任务缓存队列
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();//实际上就是mFutrueTak执行run(),run()中再执行mWorker.call()方法
                    } finally {
                        scheduleNext();
                    }
                }
            });
            //开始时,mActive为null,前面没有被激活的线程,所以第一个工作子线程从这儿开始
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

1.任务执行前的准备工作
如果是串行执行,AsyncTask提供了两个方法,来启动任务执行:

(1)
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
}
(2)
@MainThread
public static void execute(Runnable runnable) {
        sDefaultExecutor.execute(runnable);
}

如果是并行执行,AsyncTask提供了一个方法

(3)
@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);//方法(2)也就是直接执行了这个方法,没有前面的这些操作

        return this;
    }

上面有几处值得我们注意:

  • execute方法在主线程中执行
  • 如果此时线程处于running or finished状态,将会抛出异常
  • 第一个方法为final 类型,第二个为static类型,第三个为final类型

第一个方法其实就是对第三个方法进行了一个封装,将第一个参数设置成了SERIAL_EXECUTOR,这个也就是SerialExecutor的一个final类型实例。上面已经提过。而第三个方做了一些初始工作(mStatus = Status.RUNNING;onPreExecute();
mWorker.mParams = params;)之后,会去执行第二个方法。真正开始任务。也就是说它们的区别基本上是是否带输入参数,是否用并发线程池执行任务。以上是完成任务执行前的准备工作。


开始执行任务
看上面的(3)方法中的任务执行代码

exec.execute(mFuture);//exec为线程池类型,mFuture为Runnable类型,后面分析

对于一般的串行执行,上面已经给出了具体执行内容
这里再重新写一遍:

private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;//待执行的任务

        public synchronized void execute(final Runnable r) {
            //推入一个新的任务,任务内容是我们提供的runnable(mFutrue)
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }
        //从mTask队列中poll一个任务并在并行线程池中执行
        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

正是上面的offer/poll保证了串行执行顺序。而且归根结底,最后还是ThreadPoolExecute在执行任务。
3.任务状态回调
任务在执行的过程中,会有两个回调方法表示当前任务的状态

  • protected abstract Result doInBackground(Params… params);
  • protected onProgressUpdate(Progress… progress);

文章开头我们说过,AsyncTask在创建实例对象中会new 两个很重要的对象–mWorker、mFuture,通过上面的方法(1)(3)得知,我们线程池最终执行的就是mFuture这个实现了Runnable接口的对象。而此对象实际上是对mWork做了一个封装的,最终在mFuture的run()方法中执行了call()方法。之所以进行封装,完全是出于解耦的考虑:

  • FutureTask(mFuture对应的类)是对任务执行的状态进行了分装,既包括了任务,也包括了任务的执行状态(NORMAL,COMPLETING,CANCELD,EXCEPTION等等)。
  • WorkerRunnable(mWorker对应的类)是对详细任务的封装,是具体干活的。我们的doInBackground()就是在里面的call()中执行的。

AsyncTask的构造方法如下:

public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

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

以上我们就得知了doInBackground的执行时机。然后我们可以在doInBackground中合适的地方调用publishProgress方法,该方法通过Handler/Message的方法去调用onProgressUpdate(),更新UI

@WorkerThread
protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}
private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

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

####4.任务完成
如果任务正常完成,将会在call()方法中调用postResult()方法

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

和更新进度一样,也是通过Handler的方法传递给主线程,执行finish()方法

private void finish(Result result) {
        if (isCancelled()) {//由于取消而结束,最终调用onCancelded()回调
            onCancelled(result);
        } else {//由于正常结束,最终调用onPostExecute()
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;//更新状态,任务结束
    }

####5.任务取消或中断
执行AsyncTask的cancel()方法

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

停掉FutureTask线程,最终走finishCompletion()方法

public boolean cancel(boolean mayInterruptIfRunning) {
        if (!(state == NEW &&
              U.compareAndSwapInt(this, STATE, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    U.putOrderedInt(this, STATE, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }
 /**
     * Removes and signals all waiting threads, invokes done(), and
     * nulls out callable.
     */
    private void finishCompletion() {
        // assert state > COMPLETING;
        for (WaitNode q; (q = waiters) != null;) {
            if (U.compareAndSwapObject(this, WAITERS, q, null)) {
                for (;;) {//停掉所有工作线程
                    Thread t = q.thread;
                    if (t != null) {
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    q.next = null; // unlink to help gc
                    q = next;
                }
                break;
            }
        }
        //调用done()方法,该方法在AsyncTask构造方法中new FutureTask()对象时复写过
        done();

        callable = null;        // to reduce footprint
    }
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);
                }
            }
        };

最终又回到了postResult()中,和正常结束走相同的流程:

private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

最终判断mCancelled标志位,决定调用onCanceled()方法!

从上面的代码可以看出,AsyncTask给我们提供了两个Runnable实际上是别有用意的,mFuture可以看成是对mWoker的封装,mWoker中只处理单纯的工作子线程的业务逻辑,而mFuture用来处理其他事情,例如线程入列,任务取消,停止等和Task整体相关的东西,非常棒的设计。
最后,参考了一篇简书上的文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值