前段时间我有说过Handler
、Message
、Looper
三者之间的关系,它们可以解决线程间通信,可以用作子线程处理耗时操作,主线程刷新UI等。今天我要说的是另外一种解决方案:AsyncTask
。它的内部还是使用异步消息处理机制,只是Android
对这三者进行了高度的封装。如果你对异步消息处理机制还不是很清楚的话,你可以看看这篇文章:一天一Android之异步消息处理机制 。来看看AsyncTask
到底如何使用。
基本用法 首先看一下AsyncTask
的基本用法,因为AsyncTask
是一个抽象类,所以如果我们想使用它,就必须要创建一个子类去继承它,在继承时,我们可以为AsyncTask
类来指定3个泛型参数,这3个参数的用途如下:
Params 在执行AsyncTask
时需要传入的参数,可以在后台任务中使用
Progress 后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为返回值类型
Result 当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
所以,我们定义一个AsyncTask
就可以写成如下方式:
public class DownloadTask extends AsyncTask <Void ,Integer ,Void > {
...
}
这里,我们指定第一个泛型参数为Void
,表示在执行AsyncTask
的时候不需要传入参数给后台任务,第二个泛型参数我们指定为Integer
,表示使用整型数据来作为进度显示单位,第三个泛型参数指定为Void
,表示不指定反馈执行结果。
一般情况下,我们需要重写AsyncTask
中的几个方法才能完成对任务的定制。经常需要重写的方法有四个:
onPreExecute()
这个方法会在后台任务开始执行之前调用,一般我们会在这里进行一些初始化,比如显示一个进度条对话框等。此方法执行在主线程中。
doInBackground(Void… params)
这个方法中的所有代码都在子线程中执行,应当在这里进行耗时的操作,一旦完成操作,可以使用return
语句来将执行的结果返回,因为我们这里定义的返回类型是Void
,所以我们这里可以不返回或者返回null
。另外特别注意,在这个方法中是不能进行UI操作的,如果需要更新UI,比如更新下载进度等,需要调用publishProgress(Progress... values)
来完成。
onProgressUpdate(Integer… values)
当我们在后台任务中调用了publishProgress(Progress... values)
方法后,这个方法就会被执行,并且执行在主线程中,而且此方法所带的参数正是在后台任务方法中传多来的,我们一般再这个方法中更新UI,比如更新下载进度等。
onPostExecute(Void aVoid)
当后台任务执行完成并且通过return
语句返回时,这个方法就会被调用,返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些操作,比如关闭进度条对话框等。当然,此方法执行在主线程中。
我们模拟一个下载任务:
public class MainActivity extends AppCompatActivity {
private static String TAG = "MainActivity" ;
private ProgressDialog mProgressDialog;
private TextView mTextView;
@Override
protected void onCreate (Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.textView);
mProgressDialog = new ProgressDialog(this );
mProgressDialog.setMax(100 );
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false );
new DownloadTask().execute();
}
public class DownloadTask extends AsyncTask <Void ,Integer ,Void > {
@Override
protected void onPreExecute () {
mProgressDialog.show();
Log.d(TAG, "onPreExecute: " +Thread.currentThread().getName());
}
@Override
protected Void doInBackground (Void... params) {
for (int i=0 ;i<100 ;i++) {
try {
Thread.sleep(80 );
}catch (Exception e) {
e.printStackTrace();
}
publishProgress(i);
}
Log.d(TAG, "doInBackground: " +Thread.currentThread().getName());
return null ;
}
@Override
protected void onProgressUpdate (Integer... values) {
mProgressDialog.setProgress(values[0 ]);
Log.d(TAG, "onProgressUpdate: " +Thread.currentThread().getName());
}
@Override
protected void onPostExecute (Void aVoid) {
mProgressDialog.dismiss();
mTextView.setText("加载完毕" );
Log.d(TAG, "onPostExecute: " +Thread.currentThread().getName());
}
}
}
如果想启动一个任务,只需要调用如下代码即可:new DownloadTask().execute();
运行结果如下:
内部实现 那么,AsyncTask
内部是如何实现的呢?那么,就从我们的执行异步任务的起点开始,看看execute
方法:
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;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this ;
}
这段代码的意思是:首先会判断当前任务是否已经在执行,如果已经在执行则抛出异常,然后设置当前任务的状态为运行状态,调用onPreExecute()
方法,一般我们都会重写此方法,会进行一些初始化操作,当前方法还是在主线程中。然后保存参数,执行exec.execute(mFuture)
,下面我们来看看mWorker
和mFuture
。
先来看看mWorker
这个成员变量所属的类WorkerRunnable
:private static abstract class WorkerRunnable <Params , Result > implements Callable <Result > {
Params[] mParams;
}
可以看到,它只有一个成员变量,就是存储我们传入的参数的,并且它还是一个抽象类,并没有实现Callable
接口的方法。我们再来看看AsyncTask
的构造方法:
public AsyncTask () {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call () throws Exception {
mTaskInvoked.set(true );
Result result = null ;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true );
throw tr;
} finally {
postResult(result);
}
return result;
}
};
...
}
这里会对mWorker
进行初始化。这里实现了call
方法,并且设置mTaskInvoked=true
,然后调用doInBackground(mParams)
,之后又调用了postResult(result)
方法来操作后台任务返回的结果,那mWorker
的call
方法什么时候被调用呢?我们等会再来看,先来看看postResult()
方法的实现:
private Result postResult (Result result) {
@SuppressWarnings ("unchecked" )
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this , result));
message.sendToTarget();
return result;
}
终于,我们看到了我们熟悉的异步消息机制,这里传递一个message
,message.what
为MESSAGE_POST_RESULT
,message.object
为new AsyncTaskResult<Result>(this, result)
,我们再来看AsyncTaskResult
:
private static class AsyncTaskResult <Data > {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
AsyncTaskResult
只是一个简单的携带数据的对象。
看到这里,我们一定相信在某处还存在一个Handler
。在postResult(Result result)
方法中,通过getHandler()
方法获取了一个Handler
对象,我们再来看这个方法:
private static Handler getHandler () {
synchronized (AsyncTask.class) {
if (sHandler == null ) {
sHandler = new InternalHandler();
}
return sHandler;
}
}
这段代码是线程安全的,并且对sHandler
进行了初始化。通过查看sHandler
,它是一个静态变量,所以我们可以得出,在整个AsyncTask
体系中,都只存在一个Handler
。我们再来看看InternalHandler
:
private static class InternalHandler extends Handler {
public InternalHandler () {
super (Looper.getMainLooper());
}
@SuppressWarnings ({"unchecked" , "RawUseOfParameterizedType" })
@Override
public void handleMessage (Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
result.mTask.finish(result.mData[0 ]);
break ;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break ;
}
}
}
InternalHandler
类重写了handleMessage
方法,在接收到MESSAGE_POST_RESULT
消息时,执行了result.mTask.finish(result.mData[0])
,其实就是执行了AsyncTask.this.finish(result)
,在接收早MESSAGE_POST_PROGRESS
消息时,执行了result.mTask.onProgressUpdate(result.mData)
,其实是执行了AsyncTask.this.onProgressUpdate(progress)
,我们先看finish
方法:
private void finish (Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
可以看到,如果我们调用了cancel()
,则执行onCancelled()
回调。正常情况下调用的onPostExecute()
,而我们一般都会重新onPostExecute()
方法。
现在我们有个疑惑,那么后台任务在什么时候执行呢?那就要看mFuture
了。再看AsyncTask
的构造方法:
public AsyncTask () {
...
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 );
}
}
};
}
任务执行结束会调用postResultIfNotInvoked(get())
,get()
表示获取mWorker
的call
方法的返回值,即Result
,然后看postResultIfNotInvoked
方法:
private void postResultIfNotInvoked (Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
如果mTaskInvoked
不为true
,则执行postResult
,但是在mWorker
初始化时就已经将mTaskInvoked
为true
,所以一般这个postResult
执行不到。
好了,现在mWorker
和mFurture
都已经介绍完了,不过这里只是初始化了两个对象,并没有真正的执行,下面看真正调用执行的地方。
不知道大家还记得不,在executeOnExecutor
中有这样一句代码exec.execute(mFuture)
,而exec
又是传过来的,为sDefaultExecutor
。下面来看看这个sDefaultExecutor
:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
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);
}
}
}
可以看到sDefaultExecutor
其实为SerialExecutor
的一个实例,其内部维持一个任务队列,直接看其execute(Runnable runnable)
方法,将runnable
放入mTasks
队尾。并且判断当前mActive
是否为空,为空则调用scheduleNext
方法,scheduleNext
放则直接取出任务队列中的首任务,如果不为null
则传入THREAD_POOL_EXECUTOR
进行执行。
再来看THREAD_POOL_EXECUTOR
是什么:
public static final Executor THREAD_POOL_EXECUTOR;
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;
}
可以看到就是一个自己设置参数的线程池,参数为: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 ;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1 );
public Thread newThread (Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
};
private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128 );
看到这里,大家会认为背后原来有一个线程池,最大的并发数,长度等和CPU都有关系。但是,其实这是一个线性执行任务的线程池,大家再看SerialExecutor
,在execute
方法中,会判断当前mActive
是不是空,如果不是空,是不会取下一个任务的,只有当前任务执行完成,才会取下一个任务。
再来看mFuture
,在创建mFuture
的时候就已经将mWorker
传给了mFuture
,而mFuture
又是间接继承了Runnable
类,所以,mFuture
会在run
方法中去执行mWorker
的call
方法:
先来看FutureTask
类:
public FutureTask (Callable<V> callable) {
if (callable == null )
throw new NullPointerException();
this .callable = callable;
this .state = NEW;
}
构造方法中,记录了当前的callable
也就是mWorker
。我们直接看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);
}
}
我们可以清楚的看到,这里执行了mWorker
的call
方法。
还有一个方法我们没说,publishProgress
:protected final void publishProgress (Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this , values)).sendToTarget();
}
}
很简单,直接使用Handler
发送一个消息,并且携带参数:
private static class InternalHandler extends Handler {
public InternalHandler () {
super (Looper.getMainLooper());
}
@SuppressWarnings ({"unchecked" , "RawUseOfParameterizedType" })
@Override
public void handleMessage (Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
result.mTask.finish(result.mData[0 ]);
break ;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break ;
}
}
}
总结 好了,至此AsyncTask
的使用和原理我们已经说完了,再总结一下原生:
首先AsyncTask
内部维护了mWorker
和mFuture
,mWorker
是真正的执行后台任务,mFuture
是一个线程类,内部持有mWorker
对象,主要作用是让mWorker
去执行后台任务
并且AsyncTask
类内部维护一个静态的线性的线程队列SerialExecutor
,只要调用AsyncTask
的execute
方法,就会将mFuture
加入到线程队列中,然后开始执行队列中的任务。
另外AsyncTask
内部还维护了一个静态的Handler
,当mWorker
任务执行完成后,会使用Handler
开启异步消息机制发送消息,并且当更新进度时,也会发送消息。
好了,关于AsyncTask
我们已经了解的差不多了。