AsyncTask定位
AsyncTask是Android封装的一个用于线程切换的方便的工具类,属于一个抽象类,通过继承它来达到后台执行任务,并且把任务细节通知给前台view刷新显示的目的。如果用Java写Android,依然是推荐使用的,但是如果是用Kotlin官方则是推荐使用Kotlin的协程类来完成这类的任务。
AsyncTask优点
对于一个后台任务,是自己使用线程池还是自己新建线程还是用AsyncTask,这三种方式到底谁更好呢,从原理上来讲:
AsyncTask的执行原理其实等价于自己维护线程池来执行,而线程池和AsyncTask在性能上优于新建线程,这点很好理解,因为:新建线程会有新建线程实例,销毁线程实例的频繁的开销,而线程池没有
AsyncTask几个基本方法
AsyncTask实际上是调用线程池来完成自己的工作,也就是doInBackground方法。然后再通过主线程的Handler把结果返回给UI线程。
AsyncTask的主要的函数包含4个,doInBackground,onPreExecute,onPostExecute,onProgressUpdate
- doInBackground:必须重写的方法,执行后台任务,其中的代码在子线程执行
- onPreExecute:非必须重写方法,在开启子线程执行任务之前,在主线程执行的方法
- onPostExecute:非必须重写的方法,由封装后的后台任务(WorkerRunnable)通过Handler的post方法发送结果给主线程,在主线程执行
- onProgressUpdate:如果调用publish方法,则会发送当前任务进度给主线程,通过主线程调用该方法
AsyncTask内部的执行机制
之前也讲了,AsyncTask内部是通过线程池来执行后台任务的,那么具体怎么执行的呢。从它的execute看起:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
看来原理都在executeOnExecutor方法里,那么看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
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
方法里就做了三件事
- .检查当前状态,如果不是Pending状态1就抛异常
- 执行onPreExecute方法,请注意,这里还没有开新线程,所以这里是在主线程跑
- 给WorkRunnable(mWorker)设置参数,然后丢给Executor执行,这里的mFuture是对WorkRunnable(Callable)的包装,不再赘述
Executor只是一个接口规范,具体是不是开线程还要看实现,这里的Executor就是前面传入的sDefaultExecutor
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
可以看到这里的Executor的实现,实际上是SerialExecutor
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);
}
}
}
而SerialExecutor 的execute方法做了两件事:
- 封装传入的Runnable(使任务执行完后取下一个任务交给THREAD_POOL_EXECUTOR执行),然后把它添加到队列里,双端队列不是线程安全的,所以加了synchronized关键字
- 如果当前没有开始,就手动调用开始(取下一个任务交给THREAD_POOL_EXECUTOR执行)
可以看到,这里没有开新线程执行,所以这里的execute是主线程执行的,SerialExecutor 也只是实现了Executor接口,并没有开启新的线程执行任务,具体的开始新线程的地方还是在THREAD_POOL_EXECUTOR里。
private static final int CORE_POOL_SIZE = 1;
private static final int MAXIMUM_POOL_SIZE = 20;
private static final int BACKUP_POOL_SIZE = 5;
private static final int KEEP_ALIVE_SECONDS = 3;
public static final Executor THREAD_POOL_EXECUTOR;
static {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(), sThreadFactory);
threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
}
看到这里,总算看到开启新线程的玩意儿了,ThreadPoolExecutor。
在这里通过线程池开启子线程来执行我们分下来的任务。
那SERIAL_EXECUTOR 究竟起了个什么作用呢,可以看到实际上他的最大作用就是让任务串行执行,所以,AsyncTask的任务执行实际上是串行的
所以这里的线程池核心线程数实际上是1,因为有SERIAL_EXECUTOR 的存在,使得THREAD_POOL_EXECUTOR执行的任务最多就只有2个一个正在执行的任务和一个接下来要执行的任务。
需要注意的是,不管是SERIAL_EXECUTOR 还是THREAD_POOL_EXECUTOR,他们都是绑定在AsyncTask类上的静态属性,所以不管新建多少个AsyncTask的子类,他们的存在都是唯一的,而且是静态的,所以声明周期就特别长。这里来分析一下持有关系:
- WorkerRunnable内部执行AsyncTask的doInBackground,所以WorkerRunnable持有了AsyncTask的this实例。
- mFuture包裹了WorkerRunnable,所以相当于mFuture持有了AsyncTask的实例。
- SerialExecutor 个双端队列,持有一系列Runnable·,而这些Runnable会调用mFuture的run方法,所以他们持有mFuture,相当于队列持有mFuture,最后得出SerialExecutor持有了AsyncTask
那如果AsynTask持有了Context(Activity,Service)这类生命周期比较短的实例,那就会造成Context无法释放的情况。
最后执行的结果实际上在AsynTask的构造函数里就已经暴露了。
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;
}
};
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
可以看到我们的doInBackground执行完毕后,在finally 里会执行postResult,而postResult最终会调用主线程的(不传非主线程的looper则默认是主线程)Handler把结果传回给主线程执行其onPostExecute方法