我所理解的AsyncTask

常规用法

调用:

MyTask myTask =  new MyTask();//实例化
myTask.execute("url1","url2");//执行Task

myTask.cancel(true);//取消Task

实现:

class MyTask extends AsyncTask<String,Integer,Boolean>{

        public MyTask() {
            super();
            //构造
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            @MainThread//主线程
            //执行任务前会先执行此方法
            //可以做一些UI上的处理,比如提示用户
        }

        @Override
        protected Boolean doInBackground(String... params) {
            @WorkerThread//子线程
            //该类必须被复写
            //耗时操作就在该类完成

            //通过params传递值,根据需要可传递若干组
            String url1 = params[0];
            String url2 = params[1];

            //作用于 onProgressUpdate 方法
            publishProgress(x);

            //结果会被return到 onPostExecute 里
            return null;
        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {
            super.onPostExecute(aBoolean);
            @MainThread//主线程
            //任务完成后会调用该方法,前提是该Task没有被cancel
            //doInBackground 的最终结果会被传递到这里做处理,做UI更新或者其它操作
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            @MainThread//主线程
            //doInBackground 中通过 publishProgress 方法将子线程的阶段性结果传递到主线程
            //可以做关键点数据传递,或者子线程任务完成程度的发布等操作
        }

        @Override
        protected void onCancelled(Boolean aBoolean) {
            super.onCancelled(aBoolean);
            @MainThread//主线程
            //如果任务在完成前被cancel了,则会在任务完成后调用该方法
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
            @MainThread//主线程
            //源码中
            //protected void onCancelled(Result result) {
            //onCancelled();
            //}
            //如果有参的onCancelled方法没有被复写,则会调到该方法中
        }
    }

AsyncTask的三个参数分别代表:

  1. 是外界传入Task所需的值:运行所需参数
  2. 是Task运行过程中与主线程交互的值:阶段性结果
  3. 是你预期得到的结果:最终结果

这些类型用什么都是根据你的业务自己决定的。

下面分析中,调用到上面方法的地方我会用 蓝字 标出。


源码分析

第一部分 - AsyncTask构造

通过 new MyTask().execute("url1","url2"); 这句代码不难看出,源码入口就是 execute

但,所有class都有构造,最先执行的肯定也是构造,因此我们要先看下AsyncTask的构造是怎么写的:

/**
 * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
 * 创建新的异步任务。 此构造函数必须在UI线程上调用。
 */
public AsyncTask() {
    //实例化mWorker,实现了Callable接口的call方法
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            //call方法是在线程池的某个线程中执行的,而不是运行在主线程中
            //call方法开始执行后,就将mTaskInvoked设置为true,表示任务开始执行
            mTaskInvoked.set(true);

            //将执行call方法的线程设置为后台线程级别
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //在线程池的工作线程中执行doInBackground方法,执行实际的任务,并返回结果
            //noinspection unchecked
            Result result = doInBackground(mParams);
            //将当前线程中挂起的任何Binder命令刷新到内核驱动程序。
            //在执行可能会阻塞很长时间的操作之前调用此方法非常有用,以确保任何挂起的对象引用都已释放,以防止进程持续超过它所需的对象。
            Binder.flushPendingCommands();
            //将执行完的结果传递给postResult方法
            return postResult(result);
        }
    };

    //用mWorker实例化mFuture
    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            //不论经历的什么,任务最终都会执行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);
            }
        }
    };
}

WorkerRunnable里比较好理解,在 doInBackground(mParams) 中预加载要在子线程执行的代码,最后通过postResult将结果post出去。

mTaskInvoked.set(true); 这一句是标记下此Task已经被调用了,或者被实例化了。
private final AtomicBoolean mTaskInvoked = new AtomicBoolean(); 中可以看到 mTaskInvoked 是一个 AtomicBoolean:一个保证线程同步的赋值方法。可以理解为mTaskInvoked的赋值操作是线程安全的原子操作。

FutureTask结合WorkerRunnable是做异步任务的。内部处理了一些异常的结果处理,其中比较关键的就是 postResultIfNotInvoked()方法。

那么我么你看一下 postResultpostResultIfNotInvoked 两个方法:

private void postResultIfNotInvoked(Result result) {
    final boolean wasTaskInvoked = mTaskInvoked.get();
    if (!wasTaskInvoked) {//mWorker的call没有被调用
        postResult(result);
    }
}

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    //获取InternalHandler的实例,其中InternalHandler是主线程的
    //创建一个Code为 0x1 的Message
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

postResultIfNotInvoked 中只有当 Task没有被真正的invoked的时候才会调用 postResult(result); 也就是真正的任务代码没有被执行的情况下。

postResult 其实很简单,就是通过Handler将最终结果发布出去。
那么我么你看一下里面的内 getHandler() 拿到的是什么Handler呢:

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

private static class InternalHandler extends Handler {
    public InternalHandler() {
        //Looper类的getMainLooper方法是个静态方法,该方法返回主线程的Looper
        //此处用主线程的Looper初始化InternalHandler,表示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://0x1 - postResult()
                // There is only one result    发布最后结果
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS://0x2 - publishProgress()
                //发布阶段性处理结果
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

首先可以看到拿到的是 主线程 的Handler;
其次子线程与主线程的交互也很简单,就是用的Handler-Message通信。


第二部分 - execute()调用

点击 execute() 进入源码你会看到:

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

这里会调用到 executeOnExecutor(sDefaultExecutor, params) 方法,并且给了一个默认参数 sDefaultExecutor 。下面我们就说一下 executeexecuteOnExecutor 的区别吧:

源码的注释

1、execute(Params… params)

源码的注释是:
* 使用指定的参数执行任务。 任务返回它本身(this),以便调用者可以保留对它的引用。
*
* 此函数根据平台版本来决定调度任务是在一个单独的后台线程还是在线程池中。
* 当第一次引入时,AsyncTasks在单独的后台线程上依次执行。
* 从API4开始,被更改到允许多个任务并行操作的线程池。
* 从API11开始,任务将回到在单个线程上执行,以避免由并行执行引起的常见应用程序错误。
* 如果你真的想并行执行,可以通过THREAD_POOL_EXECUTOR使用有executeOnExecutor这个方法的版本;
* 但是,使用它的时候请关注那些警告的注释。

2、executeOnExecutor(Executor exec, Params… params)

源码的注释是:
* 此方法通常与THREAD_POOL_EXECUTOR一起使用,以允许多个任务在由AsyncTask管理的线程池中并行运行,
* 您也可以使用自己的Executor进行自定义行为。
*
* 警告:
* 允许多个任务从线程池并行运行通常不是想要的,因为它们的操作顺序没有定义。
* 例如,如果这些任务用于修改任何公共状态(例如由于按钮单击而写入文件),则不能保证修改的顺序。
* 没有仔细的工作,在极少数情况下,新版本的数据可能被旧版本覆盖,导致模糊的数据丢失和稳定性问题。
* 这种变化最好串行执行; 以确保不管什么版本此类工作都是序列化的,您可以通过SERIAL_EXECUTOR使用此功能。

两种Executor

从上面的两个方法的注释不难看出,AsyncTask有两种执行方式:

  • 默认的执行方式 SERIAL_EXECUTOR 启动的任务是串行执行的
myTask.execute("url1","url2");

等价于

myTask.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR,"url1","url2");
  • 另一个执行方式 THREAD_POOL_EXECUTOR 启动的任务是并行执行的
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"url1","url2");

串行执行:当启动若干个AsyncTask时,会按启动的先后顺序,一个个执行,前面的执行完才会开始执行后一个。
并行执行:当启动若干个AsyncTask时,这些任务是同时执行的,当然执行个数不会超过线程池总数。

相关源码

/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 * 可用于并行执行任务的{@link Executor}。
 */
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;
}

==================================================================

/**
 * An {@link Executor} that executes tasks one at a time in serial
 * order.  This serialization is global to a particular process.
 * 一个{@link Executor},它以串行顺序一次执行一个任务。 此序列化对于特定进程是全局的。
 */
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

//SerialExecutor实现了Executor接口中的execute方法,该类用于串行执行任务,
//即一个接一个地执行任务,而不是并行执行任务
private static class SerialExecutor implements Executor {
    //mTasks是一个维护Runnable的双端队列,ArrayDeque没有容量限制,其容量可自增长
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    //mActive表示当前正在执行的任务Runnable
    Runnable mActive;

public synchronized void execute(final Runnable r) {
    //execute方法会传入一个Runnable类型的变量r
    //然后我们会实例化一个Runnable类型的匿名内部类以对r进行封装,
    //通过队列的offer方法将封装后的Runnable添加到队尾
    mTasks.offer(new Runnable() {
        public void run() {
            try {
                //执行r的run方法,开始执行任务
                //此处r的run方法是在线程池中执行的
                r.run();
            } finally {
                //当任务执行完毕的时候,通过调用scheduleNext方法执行下一个Runnable任务
                scheduleNext();
            }
        }
    });
    //只有当前没有执行任何任务时,才会立即执行scheduleNext方法
    if (mActive == null) {
        scheduleNext();
    }
}

第三部分 - execute()内执行流程

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    //如果当前AsyncTask是未执行状态,则会抛出异常且停止执行。
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                //如果当前AsyncTask已经处于运行状态
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED:
                //如果当前AsyncTask已经完成。
                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.mParams = params;
    //Executor的execute方法接收Runnable参数,由于mFuture是FutureTask的实例,
    //且FutureTask同时实现了Callable和Runnable接口,所以此处可以让exec执行mFuture
    exec.execute(mFuture);

    //AsyncTask将自身返回
    return this;
}

注释写的很清楚了已经,需要提的两点是:

  1. Task开始执行就会执行这句 mStatus = Status.RUNNING;,而且非 RUNNING 类型的Task都会抛出异常;
  2. onPreExecute(); 是在 exec.execute(mFuture); 之前调用的,因此可以在 onPreExecute() 内做一些准备工作。

主要是 exec.execute(mFuture); 这句,执行了AsyncTask里的mFuture。从这一句就开始执行异步任务了。
回头看下上面AsyncTask构造里讲的,最后会执行到 postResult(Result result) 然后通过Handler发布两种结果,那么再看下 handleMessage(Message msg) 吧:

@SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
@Override
public void handleMessage(Message msg) {
    AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
    switch (msg.what) {
        case MESSAGE_POST_RESULT://0x1 - postResult()
            // There is only one result    发布最后结果
            result.mTask.finish(result.mData[0]);
            break;
        case MESSAGE_POST_PROGRESS://0x2 - publishProgress()
            //发布阶段性处理结果
            result.mTask.onProgressUpdate(result.mData);
            break;
    }
}

MESSAGE_POST_PROGRESS 这个就不说了,是发布阶段性结果用的,这里会调用 onProgressUpdate(Progress… values)
MESSAGE_POST_RESULT 是发布最后结果的,如何发布的呢?调用的是 finish()

private void finish(Result result) {
    if (isCancelled()) {
        //如果任务被取消了,那么执行onCancelled方法
        onCancelled(result);
    } else {
        //将结果发传递给onPostExecute方法
        onPostExecute(result);
    }
    //最后将AsyncTask的状态设置为完成状态
    mStatus = Status.FINISHED;
}

执行完根据目前Task的状态会调用 onPostExecute(result)onCancelled(result) ,最后再将Task的状态置为 FINISHED

下面是源码里Task的三种状态:

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

finish() 我们看到会根据 isCancelled() 来判断最后调用UI线程内的哪个方法,那么我们下面就讲一下AsyncTask的cancel。


第四部分 - cancel(true)真的有用吗?

/**
 * <p>Attempts to cancel execution of this task.  This attempt will
 * fail if the task has already completed, already been cancelled,
 * or could not be cancelled for some other reason. If successful,
 * and this task has not started when <tt>cancel</tt> is called,
 * this task should never run. If the task has already started,
 * then the <tt>mayInterruptIfRunning</tt> parameter determines
 * whether the thread executing this task should be interrupted in
 * an attempt to stop the task.</p>
 * 尝试取消执行此任务。
 * 如果任务已经完成,或已经被取消,或由于某种其他原因而无法取消,则取消操作无效。
 * 如果取消成功了,并且调用 cancel 时,此任务尚未启动,则此任务将不再被执行。
 * 如果任务已经开始,那么通过参数 mayInterruptIfRunning 来决定 执行该任务的线程是否应该被中断 以试图停止任务。
 *
 * <p>Calling this method will result in {@link #onCancelled(Object)} being
 * invoked on the UI thread after {@link #doInBackground(Object[])}
 * returns. Calling this method guarantees that {@link #onPostExecute(Object)}
 * is never invoked. After invoking this method, you should check the
 * value returned by {@link #isCancelled()} periodically from
 * {@link #doInBackground(Object[])} to finish the task as early as
 * possible.</p>
 * 调用此方法将导致在{@link #doInBackground(Object [])}返回后在UI线程上调用{@link #onCancelled(Object)}。
 * 调用此方法可以保证不会调用{@link #onPostExecute(Object)}。
 * 调用此方法后,应该在{@link #doInBackground(Object [])}内定期检查{@link #isCancelled()}返回的值,用以尽早结束任务。
 *
 * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
 *        task should be interrupted; otherwise, in-progress tasks are allowed
 *        to complete.
 *
 * @return <tt>false</tt> if the task could not be cancelled,
 *         typically because it has already completed normally;
 *         <tt>true</tt> otherwise
 *
 * @see #isCancelled()
 * @see #onCancelled(Object)
 */
public final boolean cancel(boolean mayInterruptIfRunning) {
    mCancelled.set(true);
    return mFuture.cancel(mayInterruptIfRunning);
}

=======================================================================

/**
 * Returns <tt>true</tt> if this task was cancelled before it completed
 * normally. If you are calling {@link #cancel(boolean)} on the task,
 * the value returned by this method should be checked periodically from
 * {@link #doInBackground(Object[])} to end the task as soon as possible.
 *如果此任务在正常完成之前被取消,则返回 true 。
 *如果您在任务上调用{@link #cancel(boolean)},则应该从{@link #doInBackground(Object [])}定期检查此方法返回的值,以尽快结束任务。
 * 
 * @return <tt>true</tt> if task was cancelled before it completed
 *
 * @see #cancel(boolean)
 */
public final boolean isCancelled() {
    return mCancelled.get();
}

cancel() 要和 isCancelled() 结合使用,如果你编码过程中使用到了 cancel() ,那么你需要在适当的位置去调用 isCancelled() 来决定任务要不要再进行下去,或者决定UI需要如何处理。

具体解释一下:

  1. 调用 cancel() 改变的是 mCancelled 这个成员变量,而非真正停掉了线程的执行,从java层停掉线程执行几乎是不可能的;
  2. isCancelled() 也是去获取成员变量 mCancelled 的值,来判断编码者是否想要cancel掉这个任务;
  3. 因此在如果有可能你需要自己在 doInBackground(Params... params) 这个方法内定期检测是否任务已经cancel掉了,来决定是否停止线程的运行;
  4. 当调用了 cancel() 后,AsyncTask最后会调用 onCancelled(result) 而不是 onPostExecute(result)

结语

首先,我们先说了AsyncTask里实现的那些方法都会在什么时候调用 —— 如何使用;
然后,我们从源码的角度分析了为什么这些方法会被调用的 —— 为什么这样使用;
知其然,知其所以然。

我对 AsyncTask.java 源码做了一部分注释,希望可以帮助你阅读代码:点击下载

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值