AsyncTask的使用及其原理
2019-08-12 本文已影响0人
pisfans
概述
Android 已封装好的轻量级异步类。内置一个线程池用于异步任务,另一个线程池用于排队(实际不是线程池)。一个默认绑定mainLooper的Handler用于UI线程。
使用
- public abstract class AsyncTask<Params, Progress, Result> Params 传参的类型,Progress 进度的类型,Result 返回结果的类型
- execute(Params... params):执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行。UI线程调用。
- onPreExecute():在execute(Params... params)被调用后立即执行,一般用来在执行后台任务前对UI做一些准备工作。
- doInBackground(Params... params) 关键方法,用于执行较为费时的操作,在onPreExecute()完成后立即执行,此方法将接收输入参数和返回计算结果。doInBackground方法接收的参数就是execute()中传入的参数:另外在执行过程中可以调用publishProgress(Progress... values)来更新进度信息。
- onProgressUpdate(Progress... values),如果调用publishProgress(Progress... values)时,此方法被执行,直接将进度信息更新到UI组件上。否则不执行
- onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果(就是doInBackground方法返回的结果)将做为参数传递到此方法中,直接将结果显示到UI组件上。
- cancel(boolean)方法,可以取消任务。这个方法会产生的影响是:之后调用的iscancelled()会返回true。
源码分析
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;
}
THREAD_POOL_EXECUTOR 是真正用于异步任务的线程池。可以看到它是静态的,也就是每次new AsyncTask()都是用的同一个线程池。
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();//ArrayDeque依赖于可变数组来实现的双端队列,可根据需求自动进行扩容。
Runnable mActive;//当前执行的任务
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {//offer向队列中插入一个元素,并返回true,如果队列已满,返回false(add在满时报错)
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {//poll取出队列头部的元素,并从队列中移除
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
}
SERIAL_EXECUTOR 是实现了Executor 接口,但内部操作是把Runnable 任务放入一个ArrayDeque(双端队列)。并且每执行完一个取出下一个任务执行。其实它不是线程池。
AsyncTask.execute()默认会调用到这个对象执行,它会一个个的取出任务再用真正的线程池THREAD_POOL_EXECUTOR执行,由此可见,用一个AsyncTask对象执行多个任务会是串行执行的。那么如何让它并行执行呢?改为调用AsyncTask.executeOnExecuter(AsyncTask.THREAD_POOL_EXECUTOR,"")就可以了。
public AsyncTask(@Nullable Looper callbackLooper) {// 默认构造方法会走到这里
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()// 一般是绑定mainLooper的内置的Handler类
: new Handler(callbackLooper);// 特殊情况
mWorker = new WorkerRunnable<Params, Result>() { // 继承callable的类
public Result call() throws Exception {
mTaskInvoked.set(true);// 调用标识,FutureTask的done方法里调postResultIfNotInvoked会判断
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);// 设置线程的优先级
//noinspection unchecked
result = doInBackground(mParams);//这里调关键方法doInBackground,执行耗时任务,并获取结果
Binder.flushPendingCommands(); //将进程中未执行的命令,一并送往CPU处理
} catch (Throwable tr) {
mCancelled.set(true);//如果运行异常,设置取消的标志
throw tr;
} finally {
postResult(result);//发送结果
}
return result;
}
};
//一个包装任务的包装类,与callable结合使用
mFuture = new FutureTask<Result>(mWorker) {
@Override
protected void done() {
try {//在执行完任务做一道检查,将没被调用的Result也一并发出.这个方法根据上面的mTaskInvoked判断
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);//如果发生异常,则将结果滞null发出.
}
}
};
}
callable与runnable的区别是,他的call方法有返回值。所以AsyncTask没有用runnable而是用的