之前有写过一篇博客,关于Android AsyncTask使用方法 AsyncTask 的使用方法,想着不能又是知其然不知其所以然的状态,本篇文章是对 AsyncTask 的深入学习。
目录:
AsyncTask 是一种轻量级的任务异步类,可以在后台子线程执行任务,且将执行进度及执行结果传递给 UI 线程。
1. AsyncTask 官方文档介绍
AsyncTask 的官方文档中有很详细的介绍,我将其进行了翻译,如下所示。
1.1 定义
AsyncTask 类让你很容易去正确使用 UI 线程,它允许你在后台子线程执行异步操作,然后将执行结果传递到UI线程,使其做出相对应的UI工作,并且这个过程我们不用去操作 Thread 与 Handler。
AsyncTask 被设计成一个围绕 Thread 和 Handler 的助手类,但并不构成一个通用的线程框架。AsyncTasks 最好用于短的耗时操作(最多几秒钟),对于长时间的耗时处理,还是需要通过线程池来处理。
异步任务由运行在后台的子线程计算定义,将其计算结果发布到 UI 线程上。异步任务由3个通用类型(Params、 Progress、Result)和四个执行步骤(onPreExecute、 doInBackground、onProgressUpdate、onPostExecute)来定义.
1.2 AsyncTask 的三个泛形参数
AsyncTask 的三个泛形参数分别是Params、Progress、Result。
- Params(传入参数):在执行 execute(Params... params) 任务方法时传入。
- Progress(执行进度):在后台计算期间发布的进度单位。
- Result(执行结果):后台的计算结果。
这三个参数不是每次都必须被定义使用,如果你不想使用,就定义为 Void。
如果三个参数都不使用,如下所以:
private static class MyAsyncTask extends AsyncTask {
···
···
}
1.3 AsyncTask的4个核心方法
当 AsyncTask 被执行时,会经历4个步骤,分别是onPreExecute()、doInBackground()、onProgressUpdate()、onPostExecute()。
其执行步骤分别是:
- onPreExecute():在 UI 线程上工作,在任务执行 doInBackground() 之前调用。此步骤通常用于设置任务,例如在用户界面中显示进度条。
- doInBackground(Params... params):在子线程中工作,在 onPreExecute() 方法结束后执行,这一步被用于在后台执行长时间的任务,Params 参数通过 execute(Params) 方法被传递到此方法中。任务执行结束后,将结果传递给 onPostExecute(Result) 方法,同时我们可以通过 publishProgress(Progress) 方法,将执行进度发送给 onProgressUpdate(Progress) 方法。
- onProgressUpdate(Progress... values):在 UI 线程上工作,会在 doInBackground() 中调用 publishProgress(Progress) 方法后执行,此方法用于在后台计算仍在执行时(也就是 doInBackgound() 还在执行时)将计算执行进度通过 UI 显示出来。例如,可以通过动画进度条或显示文本字段中的日志,从而方便用户知道后台任务执行的进度。
- onPostExecute(Result result):在 UI 线程上工作,在任务执行完毕(即 doInBackground(Result) 执行完毕)并将执行结果传过来的时候工作。
1.4 使用方法
由于 AsyncTask 是个抽象类,所以在使用的时候我们必须使用 AsyncTask 的子类,具体实现其方法。子类必须覆盖重写 doInBackground 方法,在实际使用中,通常还会覆盖重写 onPostExecute 方法,来处理结束耗时工作时该做的事情,通常是 UI 工作。具体代码可以看另一篇博客Android AsyncTask使用方法。
1.5 取消任务:
一个任务可以通过调用 cancel() 方法在任何时间取消。调用此方法后再调用 isCancelled() 方法,返回 true。调用此方法后,onPostExecute() 方法将在 onCancel() 方法返回后调用,而不是在 doInBackground() 返回结果后调用。为了确保任务被尽快取消,你应该在 doInBackground() 方法中进行周期性地检查 isCancelled() 的返回值,如果可能的话(例如在一个循环中进行检查),比如下面例子:
@Override
protected String doInBackground(String... strings) {
for (int i = 1; i <= 10; i++) {
···
//在for循环中进行周期性检查,检查异步任务是否被取消
if (isCancelled()) {
break;
}
···
}
···
return result;
}
1.6 线程规则:
为了正确的使用 AsyncTask 类, 必须遵循这些线程规则。
- AsyncTask 类必须是在 UI 线程中被加载,但在Android 4.1(API 16)开始,就能被自动加载完成。
- AsyncTask 类的实例对象必须在 UI 线程中被创建。
- execute() 方法必须是在 UI 线程中被调用。
- 不要手动调用方法 onPreExecute()、onPostExecute()、doInBackground()、onProgressUpdate()
- 任务只能执行一次(如果尝试第二次执行,将抛出异常)。
1.7 内存可观测性:
这个翻译的不好,如果错误,请指出,虚心请教
AsyncTask 保证所有的回调调用都是同步的。
doInBackground() 可以得知 onPreExecute() 的内存影响以及在 execute() 方法调用之前执行的任何其他操作,包括AsyncTask对象的构造。
onPostExecute() 可以得知 doInBackground() 方法产生的内存影响。
在执行 doInBackground() 方法调用 publishProgress() 产生的内存影响都对其相对应的 onProgressUpdate() 方法可见。但是 doInBackground() 方法继续执行时,需要注意 doInBackground() 后续的更新,不要干扰 onProgressUpdate() 的执行。
再调用 cancel() 方法产生的任何内存影响,在调用 isCancelled() 返回true之后可见。
1.8 执行顺序:
在首次引入 AsyncTasks 时,AsyncTasks 是在单个后台线程上串行执行的。然后从 Android 1.6(API 4) 开始将其更改为允许多个任务并行操作的线程池。
但从 Android 3.0.x(API 11) 开始,任务在单个线程上执行,以避免并行执行导致的常见应用程序错误。
如果确实需要并行执行,可以调用线程池
2.源码分析
我们根据 AsyncTask 的使用步骤进行分析,如果你不熟悉 AsyncTask 的使用方法,请看文章Android AsyncTask使用方法,其执行步骤如下图所示。
2.1 步骤一:创建 AsyncTask 的子类
由于 AsyncTask 是个抽象类,所以我们要创建一个子类,实现它的抽象方法。
public abstract class AsyncTask {
···
protected abstract Result doInBackground(Params... params);
···
}
2.2 步骤二:实例化 AsyncTask 子类对象
在 UI 线程中实例化一个 AsyncTask 的对象,即实例化一个我们在步骤一刚刚创建的 AsyncTask 子类的对象。
在 AsyncTask 源码中包含一个 Handler (InternalHandler)及两个线程池(SERIAL_EXECUTOR 和 THREAD_POOL_EXECUTOR),具体代码见下。
先来看一下 AsyncTask 的构造函数
/**
* 创建一个新的异步任务。这个构造函数必须在UI线程上调用。
*/
public AsyncTask(@Nullable Looper callbackLooper) {
//实例化一个 Handler 对象,如果 callbackLooper为空或者是App主线程的Looper,mHandler = getMainHandler();
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
//异步任务,是实现 Callable 接口的一个对象,可返回结果,也可能抛出异常
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
//执行任务,将 mTaskInvoked 设置为 true
mTaskInvoked.set(true);
//初始化计算结果 Result。
Result result = null;
try {
//设置当前调用的线程的优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//将参数传递给 doInBackground() 方法,并将返回的计算结果赋值给 result。
result = doInBackground(mParams);
//将当前线程中挂起的任何绑定器命令刷新到内核驱动程序。
//在执行可能会阻塞很长时间的操作之前调用此方法非常有用,可以确保释放了所有挂起的对象引用,以防止进程持有对象的时间超过需要的时间。
Binder.flushPendingCommands();
} catch (Throwable tr) {
//异常,任务取消,将 mCancelled 设置为 true。
mCancelled.set(true);
throw tr;
} finally {
//任务执行完毕,将计算结果result,发送给InternalHandler
postResult(result);
}
return result;
}
};
mFuture = new FutureTask(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 Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
//新建一个 InternalHandler 对象,指定App主线程的消息循环器。具体见 2.2.1 InternalHandler
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
2.2.1 InternalHandler
InternalHandler 负责子线程与主线程的沟通,当子线程执行完任务或者被取消任务后,通知主线程作出相对应的 UI 工作。
具体代码如下:
/**
* 定义的一个静态内部类 Handler
*/
private static class InternalHandler extends Handler {
//looper 为App主线程的消息循环器
public InternalHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
//任务执行结束(包括任务被取消),结果只有一个,并将该结果传递给 finish() 方法中。
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
//任务执行中,将执行结果(多个结果,以数组的形式保存)传递给 onProgressUpdate() 方法中。
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
private void finish(Result result) {
if (isCancelled()) {
//如果任务被取消,将计算结果传递给 onCancelled() 方法中。
onCancelled(result);
} else {
//任务执行完成后,将计算结果传递 onPostExecute() 方法中。
onPostExecute(result);
}
//将当前的任务状态设置为:FINISHED,表示该异步任务执行完毕。
mStatus = Status.FINISHED;
}
//如 postResult() 方法,发送消息给 InternalHandler,告知任务执行完毕
private Result postResult(Result result) {
//将执行结果发送给 InternalHandler 处理,根据 MESSAGE_POST_RESULT 标识符,执行 finish() 方法。
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
2.2.2 SERIAL_EXECUTOR 线程池
SERIAL_EXECUTOR 线程池,负责任务的分发工作,按串行顺序一次执行一个任务。
具体代码如下:
/**
* SERIAL_EXECUTOR 是按串行顺序一次执行一个任务的一个线程池。这种序列化对特定进程是全局的。
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
//构造一个Deque类型的数组对象(数组没有容量限制可,调整数组数量),其初始容量为16个元素。
final ArrayDeque mTasks = new ArrayDeque();
Runnable mActive;
public synchronized void execute(final Runnable r) {
//将 Runnable 任务插入到 mTasks 末尾位置
mTasks.offer(new Runnable() {
public void run() {
try {
//执行任务
r.run();
} finally {
//当前任务执行完后,执行下一个任务
scheduleNext();
}
}
});
//如果 mActive 为空,执行下一个任务
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
//取出 mTasks 队列头部元素并赋值给mActive,如果 mTasks 内容为空,返回null
if ((mActive = mTasks.poll()) != null) {
//并行执行任务
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
2.2.3 THREAD_POOL_EXECUTOR 线程池
THREAD_POOL_EXECUTOR 线程池,负责执行任务,并行执行,但其执行的任务是由 SERIAL_EXECUTOR 线程池 所分发。
具体代码如下:
//THREAD_POOL_EXECUTOR 是一个用来并行执行任务的线程池。其创建方法如下:
/**
* 核心线程数
* 我们希望在核心池中至少有2个线程,最多4个线程,希望比cpu计数少1个,以避免后台工作使cpu饱和。
*/
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
/**
* 返回Java虚拟机可用的CPU处理器数量。
*/
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
/**
* 线程池中允许的最大线程数
* 最大线程数为:Java虚拟机可用的CPU处理器数量 * 2 + 1,
*/
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
/**
* 非核心线程的存活时间
*/
private static final int KEEP_ALIVE_SECONDS = 30;
/**
*工作队列,用于存放被执行前的任务。这个队列只包含由“execute”方法提交的“Runnable”任务。
*/
private static final BlockingQueue sPoolWorkQueue =
new LinkedBlockingQueue(128);
/**
* 线程工厂,用于创建新的线程
*/
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
//请求构造一个新的线程,如果请求被拒绝,返回null, 构造成功后 mCount 自增 1。
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
/**
* 可以用来并行执行任务的 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;
}
2.3 步骤三:调用 execute(Params) 启动任务
在实例化一个 AsyncTask 的对象后,同样在 UI 线程上,调用 execute(Params) 方法,启动任务。
步骤三:调用 execute(Params) 方法执行任务。
/**
* 使用指定的参数执行任务。任务结束后返回 AsyncTask 对象,以便调用者可以保留对它的引用。
* 注意:此方法根据平台版本将任务调度到单个后台线程或线程池的队列上。在首次引入异步任务时,异步任务是在单个后台线程上串行执行的。
* 从 Android 1.6 开始,将其更改为允许多个任务并行操作的线程池。然后 Android 3.0 开始,任务返回到在一个线程上执行,以避免并行执行导致的常见应用程序错误。
* 如果你真的想要并行执行任务,你可以使用线程池去操作。
* 该方法必须在 UI 线程上被调用。
*
* @param 在UI线程上调用 execute(Params) 方法传入的参数
* @return AsyncTask的实例话对象
* @throws 如果 AsyncTask.Status == RUNNING || AsyncTask.Status == FINISHED 时,抛出 IllegalStateException 异常
*/
@MainThread
public final AsyncTask execute(Params... params) {
//调用 executeOnExecutor() 方法 指定 sDefaultExecutor 线程池来执行任务,及传入 params 参数。 关于sDefaultExecutor见 -> 分析1
return executeOnExecutor(sDefaultExecutor, params);
}
分析到这一步,我们需要知道 sDefaultExecutor 的由来:
//分析:sDefaultExecutor 的由来
//默认将 SERIAL_EXECUTOR 赋值给 sDefaultExecutor,关于 SERIAL_EXECUTOR 请看 2.2.2 SERIAL_EXECUTOR 线程池
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
从上述的代码中可以发现,原来,sDefaultExecutor = SERIAL_EXECUTOR。
SERIAL_EXECUTOR 是按串行顺序一次执行一个任务的一个线程池具体请看2.2.2 SERIAL_EXECUTOR 线程池 并且调用 scheduleNext() 方法,执行下一个任务,调用 THREAD_POOL_EXECUTOR.execute() 方法来执行任务,那 THREAD_POOL_EXECUTOR 又是什么呢?
THREAD_POOL_EXECUTOR 是用于执行任务的线程池,具体请看 2.2.3 THREAD_POOL_EXECUTOR 线程池
然后我们继续往下走,具体看一下 executeOnExecutor(sDefaultExecutor, params) 做了什么。
/**
* 此方法与线程池一起使用,以允许多个任务在 AsyncTask 管理的线程池中并行运行。
* 警告:允许多个任务在线程池中并行运行通常不是我们想要的,因为它们的操作顺序没有定义。
* 例如,如果这些任务用于修改任何公共状态(例如单击按钮写入文件),则不能保证修改的顺序。
* 在很少的情况下,新版本的数据可能被旧版本覆盖,从而导致模糊的数据丢失和稳定性问题。所以最好是串行的顺序执行这些更改。
* 该方法在 UI 线程上被调用
*
* @param exec 一个有效便利的进程级别线程池。
* @param params 在 UI 线程上调用 execute(Params) 方法,传入的参数。
* @return AsyncTask 的实例化对象
* @throws 如果 AsyncTask.Status == RUNNING || AsyncTask.Status == FINISHED 时,抛出 IllegalStateException 异常
*/
public final AsyncTask 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)");
}
}
//如果任务还未被执行过,则开始执行任务,将任务状态标记为 RUNNING 正在执行
mStatus = Status.RUNNING;
//执行 onPreExecute() 方法,具体内容由我们在 AsyncTask 子类中复写定义
onPreExecute();
//将传入的参数赋值给 mWorker.mParams (为什么给他,他要干什么)
mWorker.mParams = params;
//线程池开始执行任务,mFuture 是什么?
exec.execute(mFuture);
return this;
}
走到这一步时,发现遇到了两个新对象:mWorker、mFuture。最终也是由线程池exec.execute(mFuture) 来执行任务,那我们就先来看看 mWorker 与 mFuture 吧。
private final WorkerRunnable mWorker;
private final FutureTask mFuture;
/**
* 实现 Callable 的一个抽象函数,存储异步任务
*/
private static abstract class WorkerRunnable implements Callable {
Params[] mParams;
}
/**
* FutureTask 是一个可取消异步任务计算工作的类,在运行期间执行可调用的 Callable 任务
* @param 可调用的任务
* @throws 如果 callable 为 null,抛出 NullPointerException 异常。
*/
public FutureTask(Callable callable) {
//如果传入的 Callable 任务为空,抛出异常;否则,等待执行该任务
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW;
}
//我们再来看一下 Callable 接口
/**
* 返回结果并可能引发异常的任务。实现者定义一个没有参数的 call() 方法。
* Callable 接口类似于 Runnable,因为它们都是为那些实例可能由另一个线程执行的类设计的。但是 Runnable 不返回结果,也不能抛出已检查的异常。
* The {@link Executors} class contains utility methods to
* convert from other common forms to {@code Callable} classes.
* Executor 类包含将其他常见形式转换为 Callable 类的方法。
*/
@FunctionalInterface
public interface Callable {
/**
* 计算结果,如果无法计算,则抛出异常
*
* @return 计算结果
* @throws 如果无法计算,则抛出异常
*/
V call() throws Exception;
}
//其实 mWorker 与 mFuture 在 步骤二:实例化 AsyncTask 子类对象 中已经被实例化。
//这边为了方便大家阅读,再次展示一下代码:
public AsyncTask(@Nullable Looper callbackLooper) {
···
//异步任务,是实现 Callable 接口的一个对象,可返回结果,也可能抛出异常
mWorker = new WorkerRunnable() {
public Result call() throws Exception {
//执行任务,将 mTaskInvoked 设置为 true
mTaskInvoked.set(true);
//初始化计算结果 Result。
Result result = null;
try {
//设置当前调用的线程的优先级
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//将参数传递给 doInBackground() 方法,并将返回的计算结果赋值给 result。
result = doInBackground(mParams);
//将当前线程中挂起的任何绑定器命令刷新到内核驱动程序。
//在执行可能会阻塞很长时间的操作之前调用此方法非常有用,可以确保释放了所有挂起的对象引用,以防止进程持有对象的时间超过需要的时间。
Binder.flushPendingCommands();
} catch (Throwable tr) {
//异常,任务取消,将 mCancelled 设置为 true。
mCancelled.set(true);
throw tr;
} finally {
//任务执行完毕,将计算结果result,发送给InternalHandler
postResult(result);
}
return result;
}
};
//一个可取消异步任务计算工作的类,将 mWorker 这个 Callable 对象传递给 FutureTask, 其执行任务计算工作
mFuture = new FutureTask(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(Result) 方法,告知 InternalHandler 来处理执行结果。
//如 postResult() 方法,发送消息给 InternalHandler,告知任务执行完毕
private Result postResult(Result result) {
//将执行结果发送给 InternalHandler 处理,根据 MESSAGE_POST_RESULT 标识符,执行 finish() 方法。
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
关于 InternalHandler 请看 2.2.1 InternalHandler
现在我们可以总结一下 AsyncTask 的执行流程,我们在 Activity 中 调用 execute(Params) 方法开始执行任务,其执行顺序为:
- 在 executeOnExecutor() 方法执行 exec.execute(mFuture)
- SerialExecutor.execute(final Runnable r),这里的 Runnable r 就是 mFuture
- r.run()
- FutureTask.run()
- callable.call(),这里 callable 就是 mWorker 对象
- mWorker.call()
- doInBackground(mParams)
- postResult(result);
- InternalHandler.handleMessage()
整理了一下流程图,方便更直观的梳理执行流程,如下所示。(跟着步骤走一遍就有体会了)
2.4 额外:显示任务执行进度
当我们在执行任务时,如果我们要处理执行进度,则需在 doInBackground() 方法内调用 publishProgress(Progress) 方法。比如:
@Override
protected String doInBackground(String... strings) {
···
//通过调用publishProgress()方法,将执行进度传递给onProgressUpdate()
publishProgress(progressValue);
···
}
···
}
那 publishProgress(Progress) 方法做了什么呢? 往下看:
/**
* 这个方法在 doInBackground() 方法中调用,当后台任务还在执行时,将执行进度发布到 UI 线程。
* 每次调用这个方法,都会触发 UI 线程中的 onProgressUpdate() 方法。
*
* 如果任务被取消则不会触发 onProgressUpdate() 方法。
*
* @param values 执行进度,去更新UI
*/
@WorkerThread
protected final void publishProgress(Progress... values) {
//如果任务没有被取消,发送消息给 InternalHandler,
//根据 MESSAGE_POST_PROGRESS 标识符处理消息,调用 onProgressUpdate(Progress) 方法
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult
2.5 额外:取消任务
在主线程上通过调用 cancel(true) 方法来取消任务。
/**
* 尝试取消正在执行的任务。如果任务已经执行完毕或者已经被取消再或者出于某种原因,那就会导致尝试取消任务失败。
* 如果取消任务成功,当任务还没有开始执行时尝试取消任务,这样任务就不会被执行。
* 如果任务已经启动,那么 mayInterruptIfRunning 参数决定执行此任务的线程是否应该中断,以尝试停止任务。
*
* 调用此方法后会导致 UI 线程在 doInBackground() 方法执行结束返回结果后调用 onCancelled() 方法。
* 调用这个方法后可以保证以后永远不会调用 onPostExecute() 方法,即使 cancel() 方法返回 false, 也不会执行 onPostExecute() 方法。
* 为了尽早的发现结束任务,可以在 doInBackground() 方法内进行周期性检查 isCancelled()。
*
* 这只要求取消任务。它从不等待正在运行的后台任务终止,即使 mayInterruptIfRunning 为 true。
* @param mayInterruptIfRunning 如果为 true,说明线程执行该任务时应该被中断。否则,将允许正在进行的任务完成。
*
* @return 如果任务不能被取消(一般是因为任务已经被完成),返回 false。否则,返回 true。
*/
public final boolean cancel(boolean mayInterruptIfRunning) {
//将 mCancelled 属性设置为 true.
mCancelled.set(true);
//mFuture 执行 cancel() 方法。
return mFuture.cancel(mayInterruptIfRunning);
}
3. 总结
通过 AsyncTask 的源码我们可以发现,其原理还是 Thread 与 Handler 的实现,Android 帮我们封装了一下,所以我们可以很方便的使用。
其包含 两个线程池,一个 Handler,如下所示:
名称 | 类型 | 作用 |
---|---|---|
SERIAL_EXECUTOR | 线程池 | 分发任务,串行分发,一次只分发一个任务 |
THREAD_POOL_EXECUTOR | 线程池 | 执行任务,并行执行,执行的任务由 SERIAL_EXECUTOR 分发 |
InternalHandler | Handler | 负责子线程与主线程的沟通,通知主线程做 UI 工作 |
其之间的联系概括起来如下图所示:
到此 AsyncTask 的源码也就分析结束了,最好的理解方式就是走一遍
其实分享文章出来的最大目的正是等着有人指出我的错误,如果你发现哪里有错误,请毫无保留的指出即可,虚心请教。
另外,如果你觉得文章不错,对你有所帮助,请给我点个赞,谢谢~Peace~!