AsyncTask的深入理解
一、AsyncTask
1、初步理解
AsyncTask是一个轻量级的异步任务类,底层封装了Thread和Handler,AsyncTask不适合进行特别耗时的后台任务,特别耗时建议使用线程池。
public abstract class AsyncTask<Params,Progress,Result>
AsyncTask为泛型抽象类,其中三个泛型参数的含义为:
(1)Params:参数类型
(2)Progress:后台执行任务的进度
(3)Result:返回结果的类型
AsyncTask的5个核心方法:
(1)onPreExecute(),在主线程中执行,在异步任务执行之前,此方法会被调用,一般可以用于做一些准备工作。
(2)doInBackground(Params…params),在线程池中执行,此方法用于执行异步任务,params参数表示异步任务的输入参数。在此方法中可以通过publishProgress方法来更新任务的进度,publishProgress方法会调用onProgressUpdate方法。另外此方法需要返回计算结果给onPostExecute方法。
(3)onProgressUpdate(Progress…values),在主线程中执行,当后台任务的执行进度发生改变时此方法会被调用。
(4)onPostExecute(Result result),在主线程中执行,在异步任务执行之后,此方法会被调用,其中result参数是后台任务的返回值,即doInBackground的返回值。
(5)onCancelled():主线程中调用,当异步任务被取消时调用,onPostExecute()方法不会被调用。
示例:
private class DownloadTask extends AsyncTask<URL,Integer,Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));//调用onProgressUpdate,并把值给它
if (isCancelled())
break;
}
return totalSize;//返回值给到onPostExecute
}
protected void onProgressUpdate(Integer... progress) {
setProgress(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
要执行我们上述所写的函数时
new DownloadTask().execute(url1,url2,url3);
注意点
(1)AsyncTask必须在主线程中加载,Android4.1后ActivityThread的main方法会调用AsyncTask的init方法,来帮助我们加载,所以这一点就不用担心了。
(2)AsyncTask的对象必须要在主线程中创建。
(3)execute方法必须在UI线程调用。
(4)一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常。
(5)在Android 1.6之前,AsyncTask是串行执行任务的,Android 1.6的时候AsyncTask开始采用线程池里处理并行任务,但是从Android 3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask又采用一个线程来串行执行任务。尽管如此,在Android3.0以及后续的版本中,我们仍然可以通过AsyncTask的executeOnExecutor方法来并行地执行任务。
2、详细理解
(1)从execute方法入手
public final AsyncTask<Params,Progress,Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor,params);
}
得知里面还是调用了executeOnExecutor方法,sDefaultExecutor实际上是一个串行的线程池,一个进程中所有的AsyncTask全部在这个串行的线程池中排队执行。接下来看executeOnExecutor方法
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;
}
。。。
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(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);
}
}
};
mStatus上面的这个状态,是判断execute是不是第一次调用,如果不是则抛出异常。由上述函数可得知:(1)系统首先把AsycTask的Params参数封装成FutureTask对象(并发类,充当Runnable,Future能得到计算结果)(2)通过exec.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) {
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);
}
}
}
(3)把Futuretask交给SerialExecutor的execute方法去处理。(4)execute把FutureTask对象插入到任务队列mTasks中。(5)判断当前有没有在活动的AsyncTask任务,没有的话调用scheduleNext执行下个任务。从这里也能看出任务是一个一个执行的,因此AsyncTask为串行。
AsyncTask中有两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个
Handler(InternalHandler),其中线程池SerialExecutor用于任务的排队,而线程池
THREAD_POOL_EXECUTOR用于真正地执行任务,InternalHandler用于将执行环境从线程池切换到主线程。
上述看完任务的排队过程,接下来看doInBackground方法什么时候调用的
AsyncTask构造函数中有着一段代码:
mWorker = new WorkerRunnable<Params,Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
mTaskInvoked.set(true);表示当前任务已经被调用过,然后doInBackground方法被调用,并将结果传给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;
}
从上述函数中可得知,postResult主要通过sHandler发送了一个消息。接来下来看sHandler的是啥
private static final InternalHandler sHandler = new InternalHandler();
private static class InternalHandler extends Handler {
@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;
}
}
}
sHandler是一个静态的Handler对象,能将执行环境切换到主线程,因此这个Handler对象必须在主线程中创建,所以间接要求AsyncTask的类必须在主线程中加载,否则将无法工作。sHandler接收到MESSAGE_POST_RESULT这个消息后,会调用AsyncTask的finish方法,接来下看看这个方法的实现:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
可得知,如果AsyncTask被取消了,则调用onCancelled方法,否则则调用onPostExecute方法。至此,AsyncTask运行流程已经过完一遍。
上述说的流程是一个串行的流程,因为我们一开始执行的是AsyncTask的execute方法,里面帮我们调用executeOnExecutor(sDefaultExecutor, params);这个方法,并帮我们传入一个sDefaultExecutor,sDefaultExecutor实际上是一个串行的线程池。如果我们想要AsyncTask并行,就需要直接调用executeOnExecutor这个方法,并传入一个并行的线程池,如:
new MyAsyncTask("").
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");