「这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战」。
AsyncTask
在android中由于处理任务的需要,对线程这一概念进行了进一步地封装,为了更快速地响应用户的交互,在android中将线程分为主线程和子线程,通常4大组件的运行都是在主线程中,这也就是相关组件处理事件时间不能过长的原因,子线程的任务主要是在后台处理一些耗时比较长的任务,比如网络请求以及I/O操作等
AsyncTask其实是对Handler的进一步封装,这点在接下来的分析中可以体现出来
AsyncTask是一个抽象泛型类
public abstract class AsyncTask<Params, Progress, Result>
简单可以理解为Params是输入类型,Result是输出类型,Progress是表示进度的类型
该抽象类有以下几个主要的方法
- execute()触发异步任务的执行
- onPreExecute()这个方法在主线程中执行,主要用于在任务执行之前的一些准备工作
- protected abstract Result doInBackground(Params... params);处理异步任务,这个方法有两个事情要做,一个是将进度通过PublishProgress方法通知给onProgressUpdate方法进行进度的更新,二是将处理结果传递给onPostExecute方法
- protected void onProgressUpdate(Progress... values)任务进度更新
- protected void onPostExecute(Result result) 结果输出
由于AsyncTask类大部分是未实现的方法,接下来我们通过具体的例子来分析AsyncTask的工作流程
class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
@Override
protected Boolean doInBackground(Void... voids) {
for(int i = 0; i <= 100; i++){
publishProgress(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(isCancelled())break;
}
return true;
}
}
DownloadTask downloadTask = new DownloadTask();
downloadTask.execute();
这是一个实现后台下载的任务,启动任务的方法是实例化一个downloadTask,接着调用它的execute方法。 接着我们顺着execute方法去梳理AsyncTask的工作流程
@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}
从方法名executeOnExecutor可以看出任务交给了线程池,这一步在主线程中执行
@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
...
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
这里onPreExecute方法出现了,之前说过这个方法会在异步任务执行之前去做一些相关的预处理。 接着看execute方法,这是Executor接口方法,我们到实现Executor类中去看,这个类是sDefaultExecutor对象所属的类,查看发现是SerialExecutor
public synchronized void execute(final Runnable r) {
mTasks.offer(new Runnable() {
public void run() {
try {
r.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
可以发现,这里是将任务进一步封装后插入了mTasks中
protected synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
THREAD_POOL_EXECUTOR.execute(mActive);
}
}
同时我们也可以发现如果mActive == null 也就是说没有任务正在执行,那么就会让线程池去执行下一个任务,并且每个任务结束之后也会去调用下一个任务
接着我们回到Runnable,由上面分析最终我们执行的是这个Runnable,通过追踪Runnable的来源,我们发现
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;
}
};
最终是一个mWorker的东西,可以发现doInBackground就是我们自己要具体实现的方法,结果result最终通过postResult传递出去,注意doInBackground是运行在线程池中的,我们的结果最终要交给主线程,因此postResult必有线程切换
private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
果然,这里是采用Handler的机制去传递信息的,至于Handler的实例化,我们可以看下面代码
public AsyncTask() {
this((Looper) null);
}
public AsyncTask(@Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
}
我们之前实例化AsyncTask时调用的是空参构造,因此这里默认使用主线程的Handler