AsyncTask is designed to be a helper class around
Thread
andHandler
and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.)
AsyncTask
是用来帮助我们更好的使用 Thread
和 Handler
,最好是用于处理短时间异步任务。
使用 AsyncTask 解决异步问题
// 实现AsyncTask的子类
class MyAsyncTask(val name: String) : AsyncTask<Params, Progress, Result>() {
// 执行任务之前的准备工作,比如将进度条设置为Visible,工作在主线程
override fun onPreExecute() {
Log.d("async", "onPreExecute")
}
// 在onPreExecute()执行完之后立即在后台线程中调用
override fun doInBackground(vararg params: String?): Any? {
Log.d("async", "$name execute")
Thread.sleep(1000)
publishProgress(1)
return null
}
// 调用了publishProgress()之后,会在主线程中被调用,用于更新整体进度
override fun onProgressUpdate(vararg values: Int?) {
Log.d("async", "progress is: $values")
}
// 后台线程执行结束后,会把结果回调到这个方法中,并在主线程中被调用
override fun onPostExecute(result: Any?) {
Log.d("async", "onPostExecute")
}
}
// 调用
MyAsyncTask("one").execute("")
MyAsyncTask("two").execute("")
MyAsyncTask("three").execute("")
MyAsyncTask("four").execute("")
// 结果
19:29:01.472 1786-1805/com.taonce D/async: one execute
19:29:02.514 1786-1832/com.taonce D/async: two execute
19:29:03.868 1786-1833/com.taonce D/async: three execute
19:29:04.871 1786-1833/com.taonce D/async: four execute
AsyncTask
必须被子类实例化才能使用,其中必须要实现的方法是:doInBackground(vararg params: String?): Any?
, 其余三个方法是可选取实现的,具体的解释在上面的代码中都解释过了。还剩下三个泛型参数,下面一一来解释下:
-
Params
:执行任务所需要的信息; -
Progress
:执行任务的时候,对外发布的进度; -
Result
:任务执行完成后的结果。
到这为止,你是否发现了一个现象,在结果打印的信息中,每个日志都是相隔1s,仔细看看是不是,暂且留下一个坑放着,后面会分析给大家听。下面开始进入源码分析:
构造函数
先简单的分析下构造函数中的代码:
public AsyncTask(@Nullable Looper callbackLooper) {
// 初始化mHandler,如果callbackLooper为空或者为sMainLooper,那就使用getMainHandler()的Handler
// 否则使用传进来的callbackLooper
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
// 初始化一个WorkerRunnable对象,它是Callable的实现类,它的call()方法可以拥有返回值
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
...
}
};
// 初始化FutureTask对象,它传入了mWorker对象
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
...
}
};
}
mWorker
和 mFuture
暂时不分析,等到调用的时候我们再结合源码看,目前我们只需要知道初始化了这些对象就够了。
execute()
接着我们来看看执行一个任务的入口 execute()
:
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
// 调用executeOnExecutor(executor,params),传入的是sDefaultExecutor
return executeOnExecutor(sDefaultExecutor, params);
}
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
// 这里解释了为什么不能重复调用execute()方法
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(),它是最先调用的
onPreExecute();
// 将params传给mWorker
mWorker.mParams = params;
// 执行sDefaultExecutor.execute(runnable)方法
exec.execute(mFuture);
// 返回自身
return this;
}
我们调用 AsyncTask.execute(parasm)
方法其实就是调用了 sDefaultExecutor.execute(mFuture)
方法,我们顺藤摸瓜下去。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static class SerialExecutor implements Executor {
// 双端队列
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
// 添加到队列中,并且执行runnable.run()
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
// 执行完r.run()之后依旧会调用一次scheduleNext()方法
// 也就是循环的取队列中的Runnable,直到Runnable为空
// 从这里就可以看出,任务都是按照串行的形式来执行的,必须等待一个任务执行完毕才去取队列中的下一个,也就解释了开头的每个日志打印相隔1s的原因。
scheduleNext();
}
}
});
// mActive第一次必然为空
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
// 从队列中取出Runnable,并且让线程池去执行
// THREAD_POOL_EXECUTOR是一个线程池,配置在源码的最上方
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
SerialExecutor
就是一个实现了 Executor
的静态内部类,它的 execute(runnable)
接收 Runnable
对象,然后在 executeOnExecutor(Executor exec, Params... params)
方法中传入的是 mFuture
对象,所以在执行到 scheduleNext()
的时候,就会调用 mFuture
的 run()
方法。
FutureTask.run()
:
public void run() {
...
try {
// 拿到Callable对象
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 执行Callable.call()方法
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
// 将Callable.call()方法set()起来,可通过get()获取
set(result);
}
}...
}
FutureTask.run()
方法就是调用它的 Callable
变量的 call()
方法,在 AsyncTask
中,传入的 Callable
就是 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);
// 获取doInBackground(params)的返回值
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
// 出现异常将mCancelled设置为true
mCancelled.set(true);
throw tr;
} finally {
// 回调结果
postResult(result);
}
return result;
}
};
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}
mWorker
其实很简单,就是拿到 doInBackground(params)
的返回值,无论是否出现异常,都将结果回调到主线程,回调的操作看下面的 postResult(result)
private Result postResult(Result result) {
// getHandler()就是获取mHandler对象,默认绑定的是主线程Looper
// 而mHandler在构造函数中已经解释过,拿到的就是sHandler
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
通过 getHandler().obtainMessage()
获取一个 Message
对象,将消息的 what
设置为 MESSAGE_POST_RESULT
标记为结果不是进度,obj
设置为 AsyncTaskResult
对象,它是一个内部类,包含了 AsyncTask
和 Data[]
两个参数。通过 sHandler
将消息发送出去,然后我们看看它是怎么将 onPostExecute(result)
联系起来的。
sHandler
是 InternalHandler
的实例,我们从 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
判断了消息的类型,一种是结果,一种是进度,如果是结果的话,那么就调用 AsyncTask
的 finish()
方法;如果是进度的话,那么就调用 onProgressUpdate()
方法,这个方法在前面就已经介绍过了,就是更新整体进度的方法,我们只要看下 finish()
方法就ok了:
private void finish(Result result) {
// 任务是否被取消
if (isCancelled()) {
// 任务被取消的话,那么就调用onCancel(),这个方法也是可以重写的
onCancelled(result);
} else {
// 没有被取消就去调用onPostExecute()
onPostExecute(result);
}
// 将状态置为FINISHED
mStatus = Status.FINISHED;
}
源码分析到此的话,大致的流程已经完成了,通过 execute()
方法将需要执行的任务添加到 SerialExecutor
的双端队列中,然后让 THREAD_POOL_EXECUTOR
线程池去依次执行队列中的 FutureTask.run()
,这里需要注意,执行任务是一个一个执行,执行结束之后再去执行另外一个,这个线程池相当于单线程模式,以串行的形式来执行任务的,而不是并行来执行,整个线程池中真正工作的只有那一个核心线程。通过执行 FutureTask.run()
方法去间接执行 mWorker.call()
方法,在 call()
方法中获取 doInBackground()
方法返回的结果,最后通过 postResult()
和 InternalHandler
来将结果回调到 onPostExecute()
方法中。
源码分析的文章还在不断的更新,如果本文章你发现有不正确或者不足之处,欢迎你在下方留言或者扫描下方的二维码留言也可!
网友评论