AsyncTask 完全解析
引言
我们知道,在 Android 中,UI 线程不能执行耗时操作;在子线程中不能执行更新 UI 的操作。如果需要在子线程中进行 UI 相关的操作,那么就需要借助 Android 中的异步消息机制(比如我们熟悉的Handler消息机制)才能完成。如果我们自己写代码来进行相关的处理,不仅麻烦,而且大部分的代码都是相同的,AsyncTask 的出现就能帮我们省去大部分的代码,并且可以非常灵活方便地从子线程切换到UI线程。今天就从 AsyncTask 的简单使用到源码进行解析,一步一步看看他是怎么实现的。
AsyncTask 的简单使用
创建 AsyncTask 实现类
class DownLoadAsyncTask extends AsyncTask<String, Integer, Boolean> {
@Override
protected Boolean doInBackground(String... params) {
// 运行在子线程,执行访异步任务
Log.i("DownLoadAsyncTask", "doInBackground(): 开始执行异步任务");
// publishProgress(Integer); // 调用更新进度方法
return downLoadImage(params[0]); // 下载图片方法
}
@Override
protected void onPreExecute() {
// 运行在主线程,异步任务执行前的准备工作
Log.i("DownLoadAsyncTask", "onPreExecute(): 执行异步任务前的准备工作");
super.onPreExecute();
}
@Override
protected void onPostExecute(Boolean result) {
// 运行在主线程,异步任务执行完成之后的回调
Log.i("DownLoadAsyncTask", "onPostExecute(): 异步任务执行完成之后的回调 => " + result);
super.onPostExecute(result);
}
@Override
protected void onProgressUpdate(Integer... progresses) {
// 运行在主线程,用于更新进度
// 注意:该方法需要在doInBackground()内部调用publishProgress()方法并传递进度,否则不会执行
Log.i("DownLoadAsyncTask", "onProgressUpdate(): 用于更新进度");
super.onProgressUpdate(progresses);
}
}
AsyncTask<String, Integer, Boolean> 泛型说明:
- 第一个泛型:doInBackground() 方法的参数类型
- 第二个泛型:onProgressUpdate() 方法的参数类型
- 第三个泛型:doInBackground() 方法的返回类型 和 onProgressUpdate() 方法的参数类型
开始执行任务
String url = "https://www.baidu.com/img/bd_logo1.png";
DownLoadAsyncTask downLoadAsyncTask = new DownLoadAsyncTask();
downLoadAsyncTask.execute(url);
以上就是 AsyncTask 的基本用法。
源码解析
构造方法
public AsyncTask() {
this((Looper) null);
}
public AsyncTask(@Nullable Handler handler) {
this(handler != null ? handler.getLooper() : null);
}
/**
* @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);
}
}
};
}
构造方法说明:
- 最终调用的都是
AsyncTask(@Nullable Looper callbackLooper)
方法,但是这个方法使用了@hide
注解,开发者并不能直接调用。构造方法虽然比较长,但是他并没有任何的操作被执行,只是初始化了mHandler
、mWorker
、mFuture
几个变量,并且将mWorker
对象作为mFuture
的构造方法参数传递给了mFuture
对象。 -
mHandler
是MainHandler - 有一点需要注意:构造方法只能在UI线程中调用
构造方法中用到的部分对象说明:
- mHandler:Android Handler消息机制中的Handler对象,这里是
InternalHandler
对象,暂时记住就行了,后面会粘贴出具体实现的代码和说明class InternalHandler extends Handler:
- mWorker:实现
Callable
接口的AsyncTask
类的内部类WorkerRunnable
- mFuture:
FutureTask
对象。FutureTask
对象的继承关系- class FutureTask<V> implements RunnableFuture<V>
- interface RunnableFuture<V> extends Runnable, Future<V>
执行异步任务方法
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);// 调用内部方法
}
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; // 将状态设置为RUNNING
onPreExecute(); // 回调异步任务执行前的准备工作方法
mWorker.mParams = params; // 将传递的参数赋值给 mWorker 对象的 mParams 字段
exec.execute(mFuture); // 执行 mFuture
return this;
}
到这一步的时候,我们的任务已经切换子线程中了。
方法说明:
- 判断当前的状态,如果是
RUNNING
或者是FINISHED
就抛出异常 - 执行回调方法
onPreExecute()
,执行异步任务前的准备工作,比如显示进度对话框等 - 将
mFuture
对象 交给exec.execute(mFuture)
执行
exec.execute(mFuture)说明:
exec:即 sDefaultExecutor
;实现 Executor
接口的内部类 SerialExecutor
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) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run(); // 调用参数 Runnable 对象的 run() 方法
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
通过上面 SerialExecutor
类的定义,我们可以看到 execute()
方法调用了它的参数(即mFuture
)的run()
方法,我们继续查看mFuture
的 run()
方法
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 = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
在该方法中主要是调用了 callable
对象的 call()
方法,我们一起看看 callable
对象是从哪里来的。
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
查看 FutureTask
对象的构造方法,我们发现callable
对象是在构造方法中初始化的。好,我们现在就只要知道 FutureTask
对象是在哪里初始化的,就知道 call()
方法的具体执行内容是什么了。那么你是否还记得我们在查看 AsyncTask
类的构造方法时看到了它初始化几个变量就有FutureTask
对象,所以在这里执行的 call()
方法具体的方法体就是在AsyncTask
类的构造方法总中定义的方法体。为了方便观察,我把这段代码在粘贴一次:
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); // 执行 doInBackground 回调方法,参数在mParams的值在executeOnExecutor()中初始化了
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result); // 设置结果
}
return result;
}
};
看了这么久的代码,我们总算看到了执行任务的方法,也就是我们的重写方法 doInBackground(mParams)
返回的 Result
是结果泛型类型(第三个泛型的类型),在上面说过了三个泛型表示的意义;也就是说,到了这一步,我们的异步任务真正的开始执行了;我们接着看finally
中的代码,调用的 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;
}
在这个方法中使用了一个新的类 AsyncTaskResult
,它也是 AsyncTask
的内部类,定义如下:
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
回过头在看 postResult(Result result)
这个方法中的代码非常简单,getHandler()
方法获取到我们在构造方法中初始化的 mHandler
对象,接着获取一个 Message
对象并且创建一个 AsyncTaskResult
对象(这个对象包含了AsyncTask
类的本类对象和 doInBackground()
方法返回的结果对象2个参数)作为消息的内容,最后将这个消息发送到目标 mHandler
。上面说过了这个mHandler
是InternalHandler
,但是没有贴出具体实现的代码,那么现在我们就来看一下它到底做了些什么(InternalHandler
类是AsyncTask
类的内部类):
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;
}
}
}
这段代码我想大家都能看懂吧,最常见的 Handler
写法,通过判断 Message
的 what
进行不同的操作(任务执行完成或者更新进度)。我们只需要弄清楚 result.mTask
表示的哪一个对象就可以知道调用的方法是什么了。
在postResult(Result result)
方法中,我们知道了 Message
的 obj
字段的值是 AsyncTaskResult
对象,参数是 this
和 doInBackground()
方法返回的结果对象,很容易知道this
表示 AsyncTask
本类对象;在通过 AsyncTaskResult
的构造方法我就知道 result.mTask
就是AsyncTask
的本类对象,所以我们就知道了 result.mTask.finish(result.mData[0])
方法就是AsyncTask
类的 finish(Result result)
方法了;同理result.mTask.onProgressUpdate(result.mData)
方法就是AsyncTask
类的 onProgressUpdate(Progress... values)
方法。
看看AsyncTask
类的 finish(Result result)
方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
方法说明:
- 判断是不是取消了,如果取消了,就回调
onCancelled()
方法(开发者重写) - 如果没有取消就回调
onPostExecute(Result result)
方法(开发者重写) - 将任务的执行状态改变为
FINISHED
到此为止,我们发现的任务执行前的准备方法onPreExecute()
、任务执行方法 doInBackground
以及 取消方法 onCancelled()
和完成方法 onPostExecute()
都成功回调了;但是我们更新进度的方法 onProgressUpdate()
却并没有在哪里有调用;如果看的比较知仔细的可能已经发现了,我在第一部分简单使用中说明了:
那么我们看看 publishProgress(Progress... values)
方法:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
它和 postResult(Result result)
方法是一样的,发送一个消息,就不在多说了,回过头看一下就知道了。
以上就是 AsyncTask
的主要执行过程。
网友评论