AsyncTask是Android中常用的异步任务解决方案,在面试中面试官经常会问到有关AsyncTask相关的点,很多人只知道如何去用AsyncTask,没有深入过源码理解其原理,这在面试的时候往往对自己不利,本文从源码角度解读AsyncTask的原理及使用,相信读完本文你就可以很好地应对Android面试中有关AsyncTask的问题。
首先,AsyncTask是一个抽象类,我们使用的时候需要自定义一个类去继承AsyncTask,在继承时我们可以为AsyncTask类指定三个泛型参数,这三个参数的用途如下:
Params
Progress
Result
在AsyncTask
这个抽象类中有一个抽象方法protected abstract Result doInBackground(Params... params)
,所以如果要自定义一个自己的AsyncTask
必须实现该抽象方法,在doInBackground
中做自己的耗时操作的逻辑。
几个回调方法:
onPreExecute()
这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
doInBackground(Params…)
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return
语句来将任务的执行结果进行返回,如果AsyncTask
的第三个泛型参数指定的是Void
,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作
的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress…)
方法来完成。
onProgressUpdate(Progress…)
当在后台任务中调用了publishProgress(Progress…)
方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
onPostExecute(Result)
当后台任务执行完毕并通过return
语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
放一张AsyncTask的核心逻辑图,大家可以先看图再看后面的具体介绍,读完介绍之后再回过头来看一下这张图,理解会更深刻(建议查看大图):
如果还看不清见原图地址:https://github.com/DmrfCoder/interview/blob/master/resource/AsyncTask.png
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);
}
}
};
}
在构造方法中没有执行过多的逻辑,只是初始化了三个对象:Handler mHandler
、WorkerRunnable
、FutureTask
接着要想启动一个任务,我们就需要调用该任务的excute()
方法
excute()
方法的源码:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
在excute()
源码中调用了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;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
在该方法中先是调用了onPreExecute()
方法,因此证明了onPreExecute()
方法会第一个被调用,然后执行了exec.execute(mFuture)
方法,这个exec
实际上从excute()
方法中传入的sDefaultExecutor
。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
这里先new
出了一个SERIAL_EXECUTOR
常量,然后将sDefaultExecutor
的值赋值为这个常量,也就是说明,刚才在executeOnExecutor()
方法中调用的execute()
方法,其实也就是调用的SerialExecutor
类中的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);
}
}
}
SerialExecutor
类中有一个ArrayDeque
的队列,还有一个当前的Runnable mActive
对象,在execute()
方法中,首先会用mTasks.offer
给队列的尾部加入一个匿名的Runnable
类,在该Runnable
匿名类中执行了excute
中传入的Runnable
对象的run
方法,然后调用scheduleNext
,该方法使用mTask.poll()
取出队列头部的任务,然后调用THREAD_POOL_EXECUTOR.execute(mActive)
,这里的THREAD_POOL_EXECUTOR
实际上是一个线程池,当当前队头的run
方法执行完成之后又会在try..finally
中调用scheduleNext()
取出下一个任务进入线程池进行执行,所以可以看到,在AsyncTask
中实际上有两个线程池,一个是SerialExcutor
,另一个是THREAD_POOL_EXCUTOR
,他们两是都是静态字段,对于所有的AsyncTask
都会公用他们两,前者模仿的是单一线程池,用于做任务调度,利用队列将所有的任务排队,然后每次把队头的任务交给THREAD_POOL_EXCUTOR
去做实际的执行,
注意excute
方法中的Runnable
参数,那么目前这个参数的值是什么呢?当然就是mFuture
对象了,也就是说这里的r.run()
实际上调用的是FutureTask
类的run()方法,而我们刚才在构造mFuture
的时候传入了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;
}
};
WorkerRunnable
是一个抽象类,其实现了Callable
接口,所以在这里实现了Callable
接口需要的``call方法,
call方法里调用了
doInBackground()方法,调用完成之后在
try…finally中调用了
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;
}
在这里使用getHandler()
拿到刚才构造方法中的mHandler
对象,然后发出了一条消息,消息中携带了MESSAGE_POST_RESULT
常量和一个表示任务执行结果的AsyncTaskResult
对象。而这个mHandler
实际上是一个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;
}
}
}
可以看到在该InternalHander
的handleMessage
方法中接收到了刚才发送的消息,并根据msg.what
的不同调用了不同的逻辑,如果这是一条MESSAGE_POST_RESULT
消息,就会去执行finish()
方法,如果这是一条MESSAGE_POST_PROGRESS
消息,就会去执行onProgressUpdate()
方法。那么finish()
方法的源码如下所示:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
可以看到,如果当前任务被取消掉了,就会调用onCancelled()``方法,如果没有被取消,则调用onPostExecute()
方法,这样当前任务的执行就全部结束了。
我们注意到,在刚才InternalHandler
的handleMessage()
方法里,还有一种MESSAGE_POST_PROGRESS
的消息类型,这种消息是用于当前进度的,调用的正是onProgressUpdate()
方法,那么什么时候才会发出这样一条消息呢?相信你已经猜到了,查看publishProgress()
方法的源码,如下所示:
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
正因如此,在doInBackground()
方法中调用publishProgress()
方法才可以从子线程切换到UI线程,从而完成对UI元素的更新操作。其实也没有什么神秘的,因为说到底,AsyncTask
也是使用的异步消息处理机制,只是做了非常好的封装而已。
刚才提到的THREAD_POOL_EXECUTOR
:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 1;
private static final int KEEP_ALIVE_SECONDS = 30;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
可以看到,AsyncTask实际上是对线程池ThreadPoolExcutor
的封装,在实例化ThreadPoolExcotor
的时候传入的核心线程数是在2-4
个之间,最大线程数是cpu count*2 1
个,我们在SerialExecutor
的scheduleNext
方法中使用该线程池去真正执行任务。
AsyncTas
k默认是串行执行任务的,如果想要并行,从Android 3.0
开始AsyncTask
增加了executeOnExecutor
方法,用该方法可以让AsyncTask
并行处理任务。方法签名如下:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params)
exec
是一个Executor
对象,为了让AsyncTask
并行处理任务,第一个参数传入一个线程池对象。第二个参数params
表示的是要执行的任务的参数
刚才提到了,AsyncTask
的SerialExecutor
线程池中做的调度是串行的,也就是说同时只会有一个线程被执行,那这里的ThreadPoolExcutor THREAD_POOL_EXECUTOR
为什么没有初始化成singleThreadPool
?这点我个人也不是很理解,之前面试的时候有请教过面试官,面试官说是起到调度作用,我个人猜测可能和AsyncTask并行执行任务有关,如上所述,如果想要让AsyncTask并行执行任务需要调用executeOnExecutor并传入线程池,而这里我们可以直接传入AsyncTask帮我们实例化好的线程池对象
AsyncTask.THREAD_POOL_EXECUTOR
,这样就不用我们自己创建线程池了,比如:
Executor exec = new ThreadPoolExecutor(15, 200, 10,
TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
new DownloadTask().executeOnExecutor(exec);
当然这只是我个人的理解,欢迎大家在评论区留言发表自己的看法。
以上就是关于AsyncTask源码的入门分析,希望大家再看一遍这张图: