在Ui线程需要与子线程进行数据交互,并涉及到UI更新的时AsyncTask是最常用的办法。所以作为Android开发中最容易用到的一个类,非常有必要研习一下他的源码。这样将有助于更灵活的在应用层使用这个类。
AsyncTask常用方式举例
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
/**
* Runs on the UI thread before {@link #doInBackground}.
*
* @see #onPostExecute
* @see #doInBackground
*/
@MainThread
protected onPreExecute() {
}
/**
* Override this method to perform a computation on a background thread. The
* specified parameters are the parameters passed to {@link #execute}
* by the caller of this task.
*
* This method can call {@link #publishProgress} to publish updates
* on the UI thread.
*
* @param params The parameters of the task.
*
* @return A result, defined by the subclass of this task.
*
* @see #onPreExecute()
* @see #onPostExecute
* @see #publishProgress
*/
@WorkerThread
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));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
/**
* Runs on the UI thread after {@link #publishProgress} is invoked.
* The specified values are the values passed to {@link #publishProgress}.
*
* @param values The values indicating progress.
*
* @see #publishProgress
* @see #doInBackground
*/
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
/**
* <p>Runs on the UI thread after {@link #doInBackground}. The
* specified result is the value returned by {@link #doInBackground}.</p>
*
* <p>This method won't be invoked if the task was cancelled.</p>
*
* @param result The result of the operation computed by {@link #doInBackground}.
*
* @see #onPreExecute
* @see #doInBackground
* @see #onCancelled(Object)
*/
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
我是非常强调看源码注释的。尤其是分析源码方法的时候。一个良好的代码,注释能快速让人了解他的用途。并理解这段代码的意义和目的。
上面这个example来自AsyncTask.java的类注释。为了充分说明AsyncTask我给他额外添加了经常用到的onPreExecute。
官方注释的example我给他还额外添加了每个方法的注释。注释已经明白的讲明了以上几个方法的作用及意义。这里归纳一下:
AsyncTask<URL, Integer, Long>
我们一般在使用AsyncTask的时候都是会继承于他的.并且用泛型来做出一定的限制.
第一个参数是后台线程入口需要的参数,这里是在模拟下载的过程,所以后台线程需要的url是要作为泛型进行限制的.
第二个参数是在有进度条的情境下使用的.这个泛型限制是表示进度条每一个进度的单位类型.一般进度条使用Integer做进度展现即可.
第三个参数是当后台线程执行完之后返回给UI线程的Result的类型.
这三个参数都是选填的.如果都不需要那么均置为VOID即可.
/**
* Runs on the UI thread before {@link #doInBackground}.
*
* @see #onPostExecute
* @see #doInBackground
*/
@MainThread
protected onPreExecute() {
}
这个方法跑在UI线程,这个方法主要是做一下预先的设置工作.这里其实@MainThread
就是表示了这个方法跑在UI线程的.
/**
* Override this method to perform a computation on a background thread. The
* specified parameters are the parameters passed to {@link #execute}
* by the caller of this task.
*
* This method can call {@link #publishProgress} to publish updates
* on the UI thread.
*
* @param params The parameters of the task.
*
* @return A result, defined by the subclass of this task.
*
* @see #onPreExecute()
* @see #onPostExecute
* @see #publishProgress
*/
@WorkerThread
protected Long doInBackground(URL... urls) {
在做了必须的一些设置之后,AsyncTask的protected Long doInBackground(URL... urls)
会被调用.这个是跑在子线程的.所以耗时的任务是在这里写的.这里的...
表示方法的参数可以使一个及以上的.
有时候你在下载的时候要展现一点一点下载的过程,那就需要在执行耗时任务的同时告诉UI去更新.那就需要publishProgress()出马了.他会在自己每次被调用的时候都去通知AsyncTask的onProgressUpdate
:
/**
* Runs on the UI thread after {@link #publishProgress} is invoked.
* The specified values are the values passed to {@link #publishProgress}.
*
* @param values The values indicating progress.
*
* @see #publishProgress
* @see #doInBackground
*/
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
看,注释就是这样的意思:只要publishProgress
被调用,那onProgressUpdate
就会执行.是不是用起来很爽?
当完成了protected Long doInBackground(URL... urls)
的操作之后,protected void onPostExecute(Long result) {
就会执行.
当使用的时候就可以直接在UI线程调用:
new DownloadFilesTask().execute(url1, url2, url3);
即可
AsyncTask源码分析
new出一个AsyncTask
对象
AsyncTask源码分析的入口点就是在UI线程中调用的代码.
首先我们会new出一个AsyncTask
对象.看一下这个动作会做什么事情:
public 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));
}
};
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 occured while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
这里看似代码很长,有用的也就两个成员变量:mWorker mFuture
不妨看一下这两个对象是个什么:找到他们的继承关系:
而RunnableFuture接口是继承自Runnable接口的,所以mFuture就是一个Runnable接口
所以就有必要进入FutureTask的run方法看一下:
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();//这一行回调的事mWorker的call方法
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);//这一行最终调用的是mFuture的done方法
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
这一切就是两行注释所做的事情.那么他们什么时候真正的执行起来的呢?就是在AsyncTask执行execute动作的时候
调用execute
方法
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
会调用到executeOnExecutor
方法:他的参数是一个Executor,和需要输入到工作线程执行的参数数据
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
...
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
忽略一些过滤代码,我们看到执行了上面说到的onPreExecute
方法.这个方法是第一个被调用到的方法.这个方法是我们要自己根据设置需要而重写的.
之后就会调用Executor
参数的execute方法执行mFuture.那么这个mFuture的run方法就跑了起来.进入参数sDefaultExecutor
的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);
}
}
}
可以看到他是一个串行的Executor
现在来仔细分析这段代码.首先他造了一个队列,这个队列用来承装实现了Runnable的对象.这些对象就是等待执行的工作线程.mTasks.offer(new Runnable()
是吧一个new出来的Runnable对象加入到队列的队尾中.等待执行