AsyncTask解析
简介
AsyncTask
是一个专注于UI线程 和 后台线程 之间进行交流的辅助类。 一般用于 由后台线程发起指定计算任务,并将其结果展示在UI线程上 的场景。
目前已被弃用(因为持有context引用,有可 能造成内存泄漏,缺失回调等原因),官方推荐采用
java.util.concurrent
或者Kotlin
协程 代替。但还是值得学习下。
使用
AsyncTask
是一个抽象类,必须被子类继承使用。 继承后必须要实现doInBackground
方法,里面写需要在后台执行的任务;通常还会实现onPostExecute
,里面做UI界面想要做的操作。
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
执行AsyncTask
:
new DownloadFilesTask().execute(url1, url2, url3);
源码解析
主要跟着源码梳理流程,我使用的版本是android-30。
首先AsyncTask
是一个抽象类,带了三个泛型:
-
Params
, 执行任务execute(Params p)
时传入 的类型。 -
Progress
, 用来表示后台线程执行进度的 类型。 -
Result
,后台线程计算完后,返回结果的 类型.
public abstract class AsyncTask<Params, Progress, Result> {
private static final int CORE_POOL_SIZE = 1;
private static final int MAXIMUM_POOL_SIZE = 20;
private static final int BACKUP_POOL_SIZE = 5;
private static final int KEEP_ALIVE_SECONDS = 3;
...
}
而且看常量命名看的出来用到了线程池,核心线程1,最大线程20,维持3秒。属于即用即销的线程池,所以AsyncTask
更适用于时间较短(几秒钟)的操作。
execute
直接看execute方法。@MainThread
表示这个方法只能在主线程运行。
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
- sDefaultExecutor【先混个面熟】
sDefaultExecutor
是一个静态全局的SerialExecutor
。接着往下走。
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)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
先判断全局状态,一共有三种PENDING
(待处理)、RUNNING
(正在运行)和FINISHED
(已结束)。必须要PENDING
待处理才能往下走,然后把状态变更为RUNNING
,接着执行onPreExecute
,这是我们在自定义AsyncTask
中要重写的方法,做执行前的准备工作。
接着先把传进来的数据params
存到一个叫mWork
的东西中。 再执行exec.execute(mFuture);
。【返回的是AsyncTask
本身】
exec.execute(mFuture);
mFuture
是啥?是个FutureTask(Runnable runnable)
,里面持有mWorker
。
mFuture = new FutureTask<Result>(mWorker) { ... }
mWorker
是啥?是个继承自Callable
名字叫WorkerRunnable
的类。
mWorker = new WorkerRunnable<Params, Result>(){ ... }
WorkerRunnable
继承自Callable
,里面啥都没有,就存了个Params
数组。
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
刚才的params
就是存到了这里的。
exec就是DefaultExecutor
所以要看上面第一步👆传来的静态全局常量sDefaultExecutor
。定义为SerialExecutor
。
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);
}
}
}
这个Executor
内部有一个叫mTask
的存储Runnable
的双端队列【Deque
:两边都可以进,都可以出】。
调用execute方法无非就是往mTask
里面存了个Runnable
进去,这个Runnable
的run()
方法执行了我们传进来的mFuture
的run方法。
还有一个名叫mActive
的runnable对象。刚进来肯定是null,所以走cheduleNext()
——从mTask
中提一个出来,赋值给mActive
,如果不为空,就把他(赋值后的mActive
)放到线程池中去执行。
mActive.run
---> mFuture.run()
---> mWork.call()
一条线下来,我们要看mWork
的call方法。
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;
}
};
一眼就看到了result = doInBackground(mParams);
,这里面是我们自己写的后台要处理的运算。
然后把result
post
出去。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
Handler处理消息
可以看到底层是用的Handler,getHandler返回的是一个内部的mHandler,在构造方法中被初始化的。
public AsyncTask() {
this((Looper) null);
}
public AsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
...
}
默认为空的情况下得到的是getMainHandler()
。
private static Handler getMainHandler() {
synchronized (AsyncTask.class) {
if (sHandler == null) {
sHandler = new InternalHandler(Looper.getMainLooper());
}
return sHandler;
}
}
里面主要是加锁初始化这个自定义的Handler:InternalHandler
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;
}
}
}
InternalHandler接到消息
回忆下上一步传递消息用的是MESSAGE_POST_RESULT
,所以在handleMessage
中得到消息后走的是result.mTask.finish(result.mData[0]);
就是走task的finish方法。
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
结束后会调用onPostExecute
方法,传入result。然后我们在继承AsyncTask时继承这个方法,就得到了结果。
publishProgress进度
虽然一套流程走完了,但我们还是没看到怎么将进度更新到UI线程的。因为更新UI的起点不在AsyncTask源码中,而在用户自定义的AsyncTask中。对,就是那句publishProgress()
方法。
@WorkerThread
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
调用了这个方法后,传入进度,内部才会通过Handler将进度传递过来。代码还是在InternalHandler
中,另一种情况:result.mTask.onProgressUpdate(result.mData);
。 执行onProgressUpdate
就又跳转到了用户的逻辑去了,实现了进度的传递。
以上就是AsyncTask
工作时的全部流程。
面试问题
AsyncTask是如何实现线程的调度的?
AsyncTask.execute()方法实际是执行的SerialExecutor
的execute方法,该方法做的事就是:向一个名为mTask的Runnable队列中插入Runnable,然后再取出、执行,取出、执行,取出、执行...直到队列为空为止。
而执行用的是THREAD_POOL_EXECUTOR.execute(Runnable)
,这个Runnable是在线程池中执行的。而且这个Runnable是一个传入mWorker
的FutureTask
,所以最终执行的是mWorker【一个Callable】的call方法,call方法中放的doInBackground
,所以可以保障doInBackground
始终在后台运行。
而返回的结果是通过Handler返回到了InternalHandler
中,这个Handler传入的是Looper.getMainLooper()
,而且它是在AsyncTask初始化的时候 初始化的。这就保证了返回的onPostExecute
和onProgressUpdate
都是在主线程中执行的了。
网友评论