AsyncTask类声明
public abstract class AsyncTask<Params, Progress, Result>
AsyncTask可以更简单地使用UI线程。该类允许你在后台执行操作,且不需要操纵线程和Handler就可以将结果抛到UI线程进行处理。
AsyckTask被设计来作为线程和Handler之间的帮助类,但是并没有构成一个通用的线程框架。AsyckTasks应当用于短时操作(最多几秒钟).如果你需要保持线程存活一段时间,建议使用java.util.concurrent包中的API,例如Executor或ThreadPoolExecutor、FutureTask
一个异步任务被一个运行在后台线程,但是将结果抛出到UI线程的Computation所定义。
一个异步任务被3个泛型和4个步骤所定义:
3个泛型:
- Params
- Progress
- Result
4个步骤:
- onPreExecute()
- doInBackground()
- onProgressUpdate()
- onPostExecute()
使用
AsyckTask必须被其子类来使用,子类至少要实现一个方法doInBackground(),通常也会重写第二个onPostExecute().
下面是一个例子:
private class DownloadFilesTask 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));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
一旦一个任务被创建,通过调用:
new DownloadFilesTask().execute(url1, url2, url3);
就可以执行了。
3个泛型:
- Params:发送给任务执行的参数类型
- Progress:任务在后台执行的进度单位(Integer...)
- Result:后台任务执行的结果
如果不需要泛型,使用Void来代替。例如,不想抛出任务进度,可用Void替换Integer等
4个不步骤:
当一个异步任务在执行时,这个任务要经过4个步骤:
- onPreExecute():在任务开始执行之前触发,处于UI线程。这个步骤通常用来构建任务,显示任务进度条等工作
- doInBackground(Params... params):在onPreExecute()执行完毕之后立即调用,运行在后台线程。这个步骤通常用来执行耗时操作。异步任务的参数将传递到这个步骤中进行处理。执行结果被返回到最后一个步骤。也可以
使用publishProgress()来发布任务的进展。这些值会被发送到UI线程,在onProgressUpdate()中进行回调处理 - onProgressUpdate():调用publishProgress()之后会被触发,执行在UI线程。执行时间是不确定的。该方法被使用来显示任务进度。
- onPostExecute():后台任务结束之后触发,执行在UI线程。后台任务的结果会以一个参数的形式传递到这个步骤中
取消任务:
一个任务能在任何时候通过调用cancel()进行取消。在调用之前可调用isCancelled()方法进行判断再决定是否调用。
调用了该方法之后,onCancelled()方法将会替代onPostExecute()方法在DoInBackground()返回时被调动。为了保证该任务能被尽可能快的取消掉,应该周期性地在doInBackground()调用isCancelled()进行检查。
线程的规则:
要使这个类正常工作,必须遵循一些线程规则;
1、AsyncTask必须加载在UI线程
2、AsyncTask实例必须在UI线程创建
3、execute()方法必须在主线程执行
4、onPreExecute()、onPostExecute()、doInBackground()、onProgressUpdate()等方法不要手动调用
5、同一任务执行两次会报异常
另外插一句关于类的构造方法和代码块
new一个对象时,执行父类的静态变量和静态代码块-->自身的静态代码块-->父类的代码块-->父类的构造方法-->自身的代码块-->自身的构造方法
源码追踪:
- 四个步骤的先后执行顺序
new DownloadTask().execute(url);
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
sDefaultExecutor其实就是一个SerialExecutor:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
SerialExecutor的定义如下:
private static class SerialExecutor implements Executor {
//ArrayDeque其实就是一个双向队列
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调用execute()方法是,就是将任务包装成一个Runnable对象,然后将该任务添加进ArrayDeque任务队列,然后在通过调用scheduleNext()执行下一个。THREAD_POOL_EXECUTOR是一个线程池,允许核心线程超时。可以看到,SerialExecutor保证了任务是一个接一个执行的,只有上一个执行了,下一个才会跟着执行,保证了高并发情况下的正常执行。
继续追踪executeOnExecutor()方法:
@MainThread
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()做预备工作,接着讲mFuture当作参数传进sDefaultExecutor中,在其中会调用
scheduleNext()执行任务。就会执行 mWorker的call()方法:
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;
}
};
可以看到,在其中调用了doInBackground(mParams)方法,故而该方法第二顺序被调用了。接着会调用postResult(result):
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
可以看到,其实就是将结果包装成AsycnTaskResult通过Handler进行处理,Handler识别该操作的标识是MESSAGE_POST_RESULT:
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:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
其实就是调用finish方法:
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
可以看到,任务取消了就会回调 protected void onCancelled()方法,否则回调 onPostExecute(result)方法,接着在改变状态值。
所以回调顺序目前是:onPreExecute()-->doInBackground()-->onPostExecute().
那onProgressUpdate()是在什么时候调用的呢?官方推荐我们看publishProgress()方法:
@WorkerThread(此时还处在子线程,方法体中通过Handler转换到主线程)
protected final void publishProgress(Progress... values) {
if (!isCancelled()) {
getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
new AsyncTaskResult<Progress>(this, values)).sendToTarget();
}
}
//就会执行到这句代码:
result.mTask.onProgressUpdate(result.mData);
另外,AsycnTaskResult的数据结构:
private static class AsyncTaskResult<Data> {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data... data) {
mTask = task;
mData = data;
}
}
可以看到,对成员变量不存在get和set方法,故而mTask和mData其实就是通过构造方法赋值的。见publishProgress().
Data是一个泛型,可以是任何类型。
网友评论