AsyncTask分析
hi,大家好,我是爱吃香蕉的猴子,今天梳理一下,首先感谢郭神曾经写的文章,我也是早年通过这个文章入门的AsyncTask, 郭神的文章, 我这次也是基于代码刨析,基于Q的代码;
AsyncTask的基础用法,这里我从SystemUI找一段
public static AsyncTask<?, ?, ?> checkPattern(final LockPatternUtils utils,
final List<LockPatternView.Cell> pattern,
final int userId,
final OnCheckCallback callback) {
//todo
AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
private int mThrottleTimeout;
private List<LockPatternView.Cell> patternCopy;
@Override
protected void onPreExecute() {
// Make a copy of the pattern to prevent race conditions.
// No need to clone the individual cells because they are immutable.
patternCopy = new ArrayList(pattern);
}
@Override
protected Boolean doInBackground(Void... args) {
try {
return utils.checkPattern(patternCopy, userId, callback::onEarlyMatched);
} catch (RequestThrottledException ex) {
mThrottleTimeout = ex.getTimeoutMs();
return false;
}
}
@Override
protected void onPostExecute(Boolean result) {
callback.onChecked(result, mThrottleTimeout);
}
@Override
protected void onCancelled() {
callback.onCancelled();
}
};
task.execute();
return task;
}
先看看AsyncTask中的三个泛型参数 和四个方法:
- params: 在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
- progress: 后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
- Result: 当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
- onPreExecute: 这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
- doInBackground: 后台执行,切入子线程执行耗时操作。
- onPostExecute:当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。
- onCancelled: Task的取消;
从execute方法入手,查看源码:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
进入executeOnExecutor方法查看:
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
......
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
在这里我们找到了onPreExecute方法,但是没有doInbackground方法,现在从exec.execute(mFuture)来入手,先看exec, 通过传入的参数sDefaultExecutor,可以确定exec就是private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; 所以执行的execute方法就是:
public static final Executor SERIAL_EXECUTOR = new 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);
}
}
}
就是SerialExecutor类中的execute方法,这部分的代码后面再分析,现在找一下mFuture
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);
}
}
};
}
mFuture在AsyncTask的构造器中初始化,传入了参数mWorker,现在查看一下mWorker:
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;
}
};
在初始化mWorkder的代码中,有一个call方法,Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 设置为后台线程,执行了doInBackground后,将执行后的返回值,传递到postResult中,那postResult执行了什么? 猜测将结果传递到主线程。
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
private Handler getHandler() {
return mHandler;
}
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
如果你已经熟悉了异步消息处理机制,这里使用mHandler对象发出了一条消息,消息中携带了MESSAGE_POST_RESULT常量和一个表示任务执行结果的AsyncTaskResult对象。mHandler就是主线程handler.
提示: ArrayDeque是不支持多线程访问的;
Now 我们来讲一下刚才的SERIAL_EXECUTOR的execute方法,可以看到,SerialExecutor是使用ArrayDeque这个队列来管理Runnable对象的,如果我们一次性启动了很多个任务,首先在第一次运行execute()方法的时候,会调用ArrayDeque的offer()方法将传入的Runnable对象添加到队列的尾部,然后判断mActive对象是不是等于null,第一次运行当然是等于null了,于是会调用scheduleNext()方法。在这个方法中会从队列的头部取值,并赋值给mActive对象,然后调用THREAD_POOL_EXECUTOR去执行取出的取出的Runnable对象。之后如何又有新的任务被执行,同样还会调用offer()方法将传入的Runnable添加到队列的尾部,但是再去给mActive对象做非空检查的时候就会发现mActive对象已经不再是null了,于是就不会再调用scheduleNext()方法。
看一看offer()方法里传入的Runnable匿名类,这里使用了一个try finally代码块,并在finally中调用了scheduleNext()方法,保证无论发生什么情况,这个方法都会被调用。也就是说,每次当一个任务执行完毕后,下一个任务才会得到执行,SerialExecutor模仿的是单一线程池的效果,如果我们快速地启动了很多任务,同一时刻只会有一个线程正在执行,其余的均处于等待状态。
当然,也有解决的方案:
mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), sThreadFactory);
threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
private static final RejectedExecutionHandler sRunOnSerialPolicy =
new RejectedExecutionHandler() {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
android.util.Log.w(LOG_TAG, "Exceeded ThreadPoolExecutor pool size");
// As a last ditch fallback, run it on an executor with an unbounded queue.
// Create this executor lazily, hopefully almost never.
synchronized (this) {
if (sBackupExecutor == null) {
sBackupExecutorQueue = new LinkedBlockingQueue<Runnable>();
sBackupExecutor = new ThreadPoolExecutor(
BACKUP_POOL_SIZE, BACKUP_POOL_SIZE, KEEP_ALIVE_SECONDS,
TimeUnit.SECONDS, sBackupExecutorQueue, sThreadFactory);
sBackupExecutor.allowCoreThreadTimeOut(true);
}
}
sBackupExecutor.execute(r);
}
};
private static final int BACKUP_POOL_SIZE = 5;
不过你可能会疑惑,使用executeOnExecutor方法为什么就可以并行,因为exec是ThreadPoolExecutor不再是SerialExecutor, 可以在JDK中查看一下execute方法
//ThreadPoolExecutor#execute
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get(); //由它可以获取到当前有效的线程数和线程池的状态
/*1.获取当前正在运行线程数是否小于核心线程池,是则新创建一个线程执行任务,否则将任务放到任务队列中*/
if (workerCountOf(c) < corePoolSize){
if (addWorker(command, tre)) //在addWorker中创建工作线程执行任务
return ;
c = ctl.get();
}
/*2.当前核心线程池中全部线程都在运行workerCountOf(c) >= corePoolSize,所以此时将线程放到任务队列中*/
if (isRunning(c) && workQueue.offer(command)) { //线程池是否处于运行状态,且是否任务插入任务队列成功
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command)) //线程池是否处于运行状态,如果不是则使刚刚的任务出队
reject(command); //抛出RejectedExceptionException异常
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
/*3.插入队列不成功,且当前线程数数量小于最大线程池数量,此时则创建新线程执行任务,创建失败抛出异常*/
else if (!addWorker(command, false)){
reject(command); //抛出RejectedExceptionException异常
}
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)
补充参数解释:
- corePoolSize:核心线程池的线程数量
- maximumPoolSize:最大的线程池线程数量
- keepAliveTime:线程活动保持时间,线程池的工作线程空闲后,保持存活的时间。
- unit:线程活动保持时间的单位。
- workQueue:指定任务队列所使用的阻塞队列
6.ThreadFactory 线程池创建线程使用的工厂
经过上面的代码,我们先得出一个简单的结论:execute方法中的corePoolSize方法核心数是5,在执行中如果<5,则addWork, 这样实现了线程池的排队执行。 例如:线程池是5,则五个同时排队执行。
Code的搬运工V1.0