Android AsyncTask源码分析
Android中只能在主线程中进行UI操作,如果是其它子线程,需要借助异步消息处理机制Handler。除此之外,还有个非常方便的AsyncTask类,这个类内部封装了Handler和线程池。本文先简要介绍AsyncTask的用法,然后分析具体实现。
基本用法
AsyncTask是一个抽象类,我们需要创建子类去继承它,并且重写一些方法。AsyncTask接受三个泛型参数:
Params:指定传给任务执行时的参数的类型
Progress:指定后台任务执行时将任务进度返回给UI线程的参数类型
Result:指定任务完成后返回的结果的类型
除了指定泛型参数,还需要根据需要重写一些方法,常用的如下:
onPreExecute():这个方法在UI线程调用,用于在任务执行前做一些初始化操作,如在界面上显示加载进度控件
doInBackground:在onPreExecute()结束之后立刻在后台线程调用,用于耗时操作。在这个方法中可调用publishProgress方法返回任务的执行进度
onProgressUpdate:在doInBackground调用publishProgress后被调用,工作在UI线程
onPostExecute:后台任务结束后被调用,工作在UI线程
源码分析
下面分析这个类的实现,主要有线程池以及Handler两部分。
1、线程池
当执行一个AsyncTask的时候调用的是execute()方法,就从这个开始看:
publicfinalAsyncTask<Params,Progress,Result>execute(Params...params){
returnexecuteOnExecutor(sDefaultExecutor,params);
}
publicfinalAsyncTask<Params,Progress,Result>executeOnExecutor(Executorexec,
Params...params){
if(mStatus!=Status.PENDING){
switch(mStatus){
caseRUNNING:
thrownewIllegalStateException("Cannotexecutetask:"+"thetaskisalreadyrunning.");
caseFINISHED:
thrownewIllegalStateException("Cannotexecutetask:"+"thetaskhasalreadybeenexecuted"+"(ataskcanbeexecutedonlyonce)");
}
}
mStatus=Status.RUNNING;
//先执行onPreExecute
onPreExecute();
mWorker.mParams=params;
exec.execute(mFuture);
returnthis;
}
execute方法会调用executeOnExecutor。在这个方法中先检查任务是否已经执行或者执行结束,然后把任务标记为running。最开始执行的是onPreExecute,接着把参数赋值给mWorker对象。这个mWorker是一个Callable对象,最终被包装为FutureTask,代码如下:
privatestaticabstractclassWorkerRunnable<Params,Result>implementsCallable<Result>{
Params[]mParams;
}
mWorker=newWorkerRunnable<Params,Result>(){
publicResultcall()throwsException{
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspectionunchecked
returnpostResult(doInBackground(mParams));
}
};
mFuture=newFutureTask<Result>(mWorker){
@Override
protectedvoiddone(){
try{
postResultIfNotInvoked(get());
}catch(InterruptedExceptione){
android.util.Log.w(LOG_TAG,e);
}catch(ExecutionExceptione){
thrownewRuntimeException("AnerroroccuredwhileexecutingdoInBackground()",
e.getCause());
}catch(CancellationExceptione){
postResultIfNotInvoked(null);
}
}
};
从上面的代码可以看出,在mWorker对象中的call()方法会调用doInbackground,返回值交给postResult方法,这个方法通过Handler发送消息,这一点稍后再详细分析。
在mWorker对象被封装成FutureTask之后交由线程池执行,从execute方法可以看出,使用的是sDefaultExecutor,它的值默认为SERIAL_EXECUTOR,也就是串行执行器,实现如下:
privatestaticclassSerialExecutorimplementsExecutor{
//线性双向队列,用来存储所有的AsyncTask任务
finalArrayDeque<Runnable>mTasks=newArrayDeque<Runnable>();
//当前正在执行的AsyncTask任务
RunnablemActive;
publicsynchronizedvoidexecute(finalRunnabler){
//将新的AsyncTask任务加入到双向队列中
mTasks.offer(newRunnable(){
publicvoidrun(){
try{
//执行AsyncTask任务
r.run();
}finally{
//当前任务执行结束后执行下一个任务
scheduleNext();
}
}
});
if(mActive==null){
scheduleNext();
}
}
protectedsynchronizedvoidscheduleNext(){
//从任务队列中取出队列头部的任务,如果有就交给并发线程池去执行
if((mActive=mTasks.poll())!=null){
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
publicstaticfinalExecutorTHREAD_POOL_EXECUTOR
=newThreadPoolExecutor(CORE_POOL_SIZE,MAXIMUM_POOL_SIZE,KEEP_ALIVE,
TimeUnit.SECONDS,sPoolWorkQueue,sThreadFactory);
在上面的代码中,如果有任务执行,那么SerialExecutor的execute方法会被调用,它的逻辑是把Runnable对象加入ArrayDeque队列中,然后判断mActivie是否为空。第一次执行时mActive当然为空,所以执行scheduleNext,其实就是取出任务队列中的第一个任务交给线程池(THREAD_POOL_EXECUTOR)执行。加入mTask队列的Runnable对象的run方法里最终一定会调用scheduleNext,那么又会从任务队列中取出队头任务执行。这样便实现了单线程顺序执行任务,所以在AsyncTask中默认启用的是单线程执行,只有上一个任务执行后才会执行下一个任务。如果想要启用多线程执行任务,可以直接调用executeOnExecutor(Executorexec, Params...params),这里的Executor参数可以使用AsyncTask自带的THREAD_POOL_EXECUTOR,也可以自己定义。
2、Handler
AsyncTask内部用Handler传递消息,它的实现如下:
privatestaticclassInternalHandlerextendsHandler{
@SuppressWarnings({"unchecked","RawUseOfParameterizedType"})
@Override
publicvoidhandleMessage(Messagemsg){
AsyncTaskResultresult=(AsyncTaskResult)msg.obj;
switch(msg.what){
caseMESSAGE_POST_RESULT:
//Thereisonlyoneresult
result.mTask.finish(result.mData[0]);
break;
caseMESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
如果消息类型是任务执行后的返回值(MESSAGE_POST_RESULT)将调用finish()方法:
privatevoidfinish(Resultresult){
if(isCancelled()){
onCancelled(result);
}else{
onPostExecute(result);
}
mStatus=Status.FINISHED;
}
从上面可以知道,如果任务取消了,将调用onCancelled,否则调用onPostExecute,所以一个AsyncTask任务如果取消了,那么onPostExecute将不会得到执行。
如果消息类型是执行进度(MESSAGE_POST_PROGRESS)将调用onProgressUpdate,这个方法默认是空方法,我们可以根据自己的需要重写。
总结
AsyncTask的主要逻辑就如上面所分析的,总结几个需要注意的地方:
1)、 AsyncTask的类必须在UI线程加载(从4.1开始系统会帮我们自动完成)
2)、 AsyncTask对象必须在UI线程创建
3)、 execute方法必须在UI线程调用
4)、 不要手动调用onPreExecute()、doInBackground、onProgressUpdate方法
5)、 一个任务只能被调用一次(第二次调用会抛出异常)
其它还有一些细节可以自行研究源码,另外推荐几篇不错的文章:
AndroidAsyncTask完全解析,带你从源码的角度彻底理解