1、什么是AsyncTask
1、是一种轻量级的异步任务类;
2、是一个封装了线程池和Handler的异步框架;
3、使用它可以更加方便的执行后台任务以及在主线程访问UI,但他不适合进行特别耗时的后台任务;
今天我们来回顾复习下AsyncTask, 它是Android 一种轻量级的异步任务类,从实现来说,AsyncTask封装了线程池和Handler。它可以在线程池中执行后台任务,把执行的进度和结果传递给主线程并在主线程中更新UI,通过AsyncTask可以更加方便的执行后台任务以及在主线程访问UI,但他不适合进行特别耗时的后台任务。
一、什么是AsyncTask?
1、是一种轻量级的异步任务类;
2、是一个封装了线程池和Handler的异步框架;
3、使用它可以更加方便的执行后台任务以及在主线程访问UI,但他不适合进行特别耗时的后台任务;
二、AsyncTask的使用方法
1、3个参数
- Params:执行AsyncTask时,后台任务需要传入的参数,在doInBackground中取出使用。
- Progress:后台任务执行时,如果需要显示当前进度,则制定进度的数据类型。
- Result:后台任务执行完成后的返回值类型。
2、5个方法
-
onPreExecute:
在主线程中执行,在后台任务执行前调用,通常用于做一些准备操作。 -
doInBackground(Params… params):
在线程池中执行,用于执行后台任务;params 参数是 execute(Params… params)方法中传递的参数。在此方法中可以调用 publishProgress 方法来更新任务的进度,publishProgress方法会调用onProgressUpdate方法。 -
publishProgress(Progress… values):
用于更新任务的进度,需要手动调用,publishProgress方法会调用onProgressUpdate方法;values参数为设置的进度值。 -
onProgressUpdate(Progress… values):
在主线程中执行,当后台任务的执行进度发生改变时,会被调用,values参数为进度值。 -
onPostExecute(Result result):
在主线程中执行,当后台任务执行完成时,会被调用。result 的值是doInBackground的返回值。
三、AsyncTask的机制原理
- AsyncTask 的本质是一个静态的线程池,可以执行不同的异步任务,这些任务提交到静态的线程池中执行。
- 线程池中的工作线程执行doInBackgroup(Params… params)方法执行异步任务。
- 当任务状态改变后,工作线程向UI线程发送消息,AsyncTask内部的InternalHandler 响应这些消息,并调用相关的回调函数。
四、AsyncTask的注意事项
1、内存泄漏
-
原因:非静态内部类持有外部类的匿名引用,导致Activity无法释放。
-
解决:
- AsyncTask内部持有外部Activity的弱引用。
- AsyncTask改为静态内部类。
- AsyncTask.cancel()。
2、生命周期
不受Activity生命周期的影响,在Activity销毁之前,取消AsyncTask的运行,以此来保证程序的稳定。
3、结果丢失
由于屏幕旋转、Activity在内存紧张时被回收等情况下,Activity会被重新创建,此时,还在运行的AsyncTask会持有一个Activity的非法引用即之前的Activity实例。导致onPostExecute()没有任何作用。
4、并行or串行
- Android 1.6之前,默认采用串行执行任务;
- Android 1.6~ 2.3 ,默认采用并行执行任务;
- Android 3.0,默认采用串行执行任务,如果需要改为并行,可以调用AsyncTask的executeOnExecutor()来执行任务即可。
5、AsyncTask的使用限制
- AsyncTask的对象必须在主线程中创建;
- execute 方法必须在UI线程调用;
- 不要在程序中手动调用 onPreExecute、onPostExecute、 doInBackground、onProgressUpdate方法;
- 一个AsyncTask对象只能调用一次excute()方法,执行一次,否则会报异常。
AsyncTask的机制原理
1.AsyncTask本质上是一个静态的线程池,其派生出来的子类可以实现不同的异步任务,这些任务都会提交到线程池中去执行
2.耗时操作是在doInBackground中执行的
3.当任务状态(pendding,running,finished)改变后,工作线程会向ui线程发送消息,AsyncTask内部的InternalHandler会响应这些消息并执行相关回调函数
AsyncTask注意事项
1、AsyncTask不适合特别耗时的任务
AsyncTask的生命周期和Activity的生命周期不同步,Activity销毁了但是AsyncTask中的任务还是会继续执行完毕,一个最典型的例子就是Activity的横竖屏切换,AsyncTask中引用的Activity不是当前的Activity,onPostExecute()中执行的仍然是上一个Activity。还有一个原因是因为AsyncTask在执行长时间的耗时任务时也会持有一个Activity对象,即使这个Activity已经不可见了,Android也无法对这个Activity进行回收,导致内存泄露。
2、AsyncTask只能在主线程中创建以及使用
AsyncTask被用于执行异步任务,然后更新UI,所以最后的onPostExecute()方法执行在创建该AsyncTask对象的线程中,如果不在主线程中创建以及使用,就达不到更新UI的目的。
3、一个AsyncTask对象只能执行一次
一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常
4、AsyncTask在不同的Android版本下的并行和串行问题
关于AsyncTask的并行和串行问题,在不同的API下是有不同的。在Android1.6之前,AsyncTask是串行执行任务的;到了Android1.6时,开始采用线程池来并行执行任务;在Android3.0之后的版本中,AsyncTask又开始用一个线程串行执行任务。虽然Android3.0之后采用串行方式执行任务,但我们可以通过AsyncTask的executeOnExecutor(exe,params),自定义一个线程池来并行执行任务。
五、AsyncTask 源码解析
5.1、执行execute()方法
执行AsyncTask 的时候会执行方法 downloadTask?.execute(downloadUrl)
,我们再看看这个方法execute()实际上是执行 executeOnExecutor()
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
···
@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;
}
5.2、AsyncTask的三种状态
这里面Status 是AsyncTask 内部的三种状态,如果状态不是Status.PENDING
就会报错,这也是为什么AsyncTask只能执行一次的原因。
/**
* 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,
}
5.3、执行onPreExecute()方法
紧接着先执行 onPreExecute() ,在这里我们看到了AsyncTask 重写的第一个方法了,我们一般在这里做一些初始化操作。我们从这里可以看到这个方法是运行在主线程中。
接下来下面的这两行代码你可能不知道是干嘛的。mWork和mFuture实际上是在AsyncTask构造函数中实例化的。
mWorker.mParams = params;
exec.execute(mFuture);
5.4、AsyncTask构造函数的分析
我们继续来看看AsyncTask的构造函数、这里面只是初始化三个对象mHandler 、mWorker 、mFuture
/**
* Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*
* @hide
*/
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() {
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);
}
}
};
}
5.5、AsyncTask的InternalHandler
mHandler 是AsyncTask的静态内部类InternalHandler,我们发现它是用来更新进度,以及任务完成时的做一些操作。
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 是什么,点进去看发现它实现了RunnableFuture接口,而RunnableFuture又是继承Runnable接口的,原来mFuture 是个新的线程,我们看看他的构造函数和run方法。在这里终于发现原来mWorker作为参数callable传入到FutureTask中,最后c.call()确实是在子线程的run()方法中调用的。这里doInBackground的谜底也接开了。
...
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
...
public void run() {
if (state != NEW ||
!U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
5.6、exec.execute(mFuture)分析
在上述的5.1方法中我们看到执行完onPreExecute()方法后会执行exec.execute()方法,我们看看它做了什么:
exec 是一个线程池 sDefaultExecutor ,mFuture作为参数传入到SerialExecutor 这个静态内部类中。r.run()即是mFuture的执行。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
...
private static volatile Executor sDefaultExecutor = 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);
}
}
}
线程池的知识这里不展开了,这里面可能大家会有疑问怎么AsyncTask的另外几个重写的方法没有执行呢,我们看看它在哪
5.7、onProgressUpdate方法
在doInBackground()中是不可以进行UI操作的,如果需要更新UI,比如说反馈当前任务的执行进度,可以调用publishProgress()方法完成,这个方法是运行在主线程的。我们从publishProgress传入的参数比如进度最终在重写onProgressUpdate的方法中处理。
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
...
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
...
@MainThread
protected void onProgressUpdate(Progress... values) {
}
5.8、onPostExecute方法
在5.4节AsyncTask的构造函数中我们可以看到调用流程是:postResultIfNotInvoked () --> postResult() -->finish()–>onPostExecute()
···
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 void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
···
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
···
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
···
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
5.9、AsyncTask的线程池
AsyncTask中还存在另一个线程池executeOnExecutor方法,但是在API28的点击去看发现最后用的还是这个SerialExecutor 单线程。我们看这个方法
当存在10个任务的时候,第一个任务进来,mActive为空执行scheduleNext,在scheduleNext方法中取出线程头部添加到线程池中,然后复制给mActive,当第二个任务进来时,mActive不为空,也就是说不执行scheduleNext()方法,所以只有等到第一个任务run方法执行完之后调用finally中的scheduleNext()才会执行下一个任务,所以来说其实还是单线程线性执行,一个接一个。
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);
}
}
}
文章到这里基本上就结束了,有疑问的小伙伴儿可以评论去留言
本文参考
1、AsyncTask面试知识小结
2、Android面试系列之异步消息处理相关
3、Android – AsyncTask源码解析
4、Android AsyncTask完全解析,带你从源码的角度彻底理解