AsyncTask
基本用法
AsyncTask是一个抽象类,使用时必须创建子类继承它,在继承时我们可以为其指定三个泛型参数:
泛型参数 | 描述 |
---|---|
Params | 在执行AsyncTask时需要传入的参数,可用于后台任务中使用。 |
Progres | 后台执行任务时,如果界面上面要显示当前任务进度,则使用该泛型为进度单位。 |
Result | 当任务执行完毕后,如果需要对结果进行返回,则使用该泛型作为返回值类型。 |
因此,一个简单的自定义的AyncTask就可以写成如下方式:
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
……
}
这里我们把AsyncTask的第一个泛型参数指定为Void,表示在执行AsyncTask的时候不需要传入参数给后台任务。第二个泛型参数指定为Integer,表示使
用整型数据来作为进度显示单位。第三个泛型参数指定为Boolean,则表示使用布尔型数据来反馈执行结果。
当然我们目前定义的DownloadTask还是一个空任务,我们还需要重写四个方法即可:
方法 | 描述 |
---|---|
onPreExecute() | 后台任务开始前调用,用于界面上的一些初始化操作。 |
doInBackground() | 在子线程中运行,用于处理耗时的操作,任务使用return来返回结果,通过Result泛型来判断是否有结果返回。如果想反馈实时任务进度,可以使用publishProgress()完成。 |
onProgressUpdate() | 在后台任务调用publishProgress()后调用,该方法可以根据实时进度来更新UI操作。 |
onPostExecute() | 后台任务执行完毕后调用,会根据返回的结果进行一系列操作。 |
因此,一个比较完整的自定义AsyncTask就可以写成如下格式:
class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
@Override
protected void onPreExecute() {
progressDialog.show();
}
@Override
protected Boolean doInBackground(Void... params) {
try {
while (true) {
int downloadPercent = doDownload();
publishProgress(downloadPercent);
if (downloadPercent >= 100) {
break;
}
}
} catch (Exception e) {
return false;
}
return true;
}
@Override
protected void onProgressUpdate(Integer... values) {
progressDialog.setMessage("当前下载进度:" + values[0] + "%");
}
@Override
protected void onPostExecute(Boolean result) {
progressDialog.dismiss();
if (result) {
Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();
}
}
}
如果想要启动这个任务,只需要简单地调用以下代码即可:
new DownloadTask().execute();
AsyncTask原理
Android提供了 AsyncTask,它使得异步任务实现起来更加简单,代码更简洁。下面首先来看AsyncTask的定义,如下所示:
public abstract class AsyncTask<Params, Progress, Result> {
// ......
}
AsyncTask是一个抽象的泛型类,它有3个泛型参数,分别为Params、Progress和Result,其中Params为 参数类型,Progress为后台任务执行进度的类型,
Result为返回结果的类型。如果不需要某个参数,可以将其设置为Void类型。
AsyncTask中有4个核心方法,如下所示:
onPreExecute():在主线程中执行。一般在任务执行前做准备工作,比如对 UI 做一些标记。
doInBackground(Params...params):在线程池中执行。在 onPreExecute方法执行后运行,用来执 行较为耗时的操作。
onProgressUpdate(Progress...values):在主线程中执行。当调用 publishProgress(Progress...values)时,此方法会将进度更新到UI组件上。
onPostExecute(Result result):在主线程中执行。当后台任务执行完成后,它会被执行。
AsyncTask是对Handler与线程池的封装。使用它的方便之处在于能够更新用户界面,当然这里更新用户界面的操作还是在主线程中完成的,但是由于AsyncTask
内部包含一个Handler,所以可以发送消息给主线程让它更新UI。(使用到线程池,减少线程的开销)
AsyncTask的局限性
AsyncTask的优点在于执行完后台任务后可以很方便的更新UI,然而使用它存在着诸多的限制。先抛开内存泄漏问题,使用AsyncTask主要存在以下局限性:
1、在Android 4.1版本之前,AsyncTask类必须在主线程中加载,这意味着对AsyncTask类的第一次访问必须发生在主线程中;
2、在Android 4.1以后不存在这一限制,因为ActivityThread的main方法中会自动加载AsyncTask
3、AsyncTask对象必须在主线程中创建
4、AsyncTask对象的execute方法必须在主线程中调用
5、一个AsyncTask对象只能调用一次execute方法
接下来,我们从源码的角度去探究一下AsyncTask的工作原理,并尝试着搞清楚为什么会存在以上局限性。
AsyncTask工作原理
首先,让我们来看一下AsyncTask类的构造器都做了些什么:
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
// WorkerRunnable实现了Callable接口,并实现了它的call方法,在call方法中调了doInBackground(mParams)来处理任务并得到结果,
// 并最终调用postResult将结果投递出去。
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;
}
};
// FutureTask是一个可管理的异步任务,它实现了Runnable和Futrue这两个接口。
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);
}
}
};
}
当要执行AsyncTask时,需要调用它的execute方法,代码如下所示:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
execute方法又调用了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();
// 将AsyncTask的参数传给WorkerRunnable
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
从前面可以知道WorkerRunnable作为参数传递给了FutureTask,因此,参数被封装到FutureTask中。
接下来会调用exec的execute方法,并将mFuture也就是前面讲到的FutureTask传进去。这里exec是传进来的参数 sDefaultExecutor,它是一个串行的线程池 SerialExecutor(通过debug获得),其代码如下所示:
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
Runnable mActive;
public synchronized void execute(final Runnable r) {
// 调用SerialExecutor的execute方法时,会将FutureTask加入到mTasks 中。
mTasks.offer(new Runnable() {
public void run() {
try {
// 执行了FutureTask的run方法,它最终会调 用WorkerRunnable的call方法
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
该类是AsyncTask的内部类,AsyncTask的原理比较麻烦,待后续整理。