进阶 | AsyncTask 源码浅析
文章图片
AsyncTask
方便我们从异步工作切换到UI线程。虽然是异步的但是依然不建议让AsyncTask
执行过大耗时操作(超过10秒),如果需要让一个线程长时间保持运行,请用线程池。AsyncTask
有三个泛型四个方法需要注意。
简单介绍 三个泛型:Params
用于初始化,Progress
用于设置进度,Result
返回的结果,如果没有参数则设置为Void
。
四个方法:
onPreExecute()
,任务执行之前操作doInbackground(Params)
,后台线程执行onProgressUpdate(Progress)
,任务执行过程中,主线程onPostExecute()
,任务执行完成- 补充,
onCancelled()
,取消任务
class DownloadTask extends AsyncTask {
@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();
}
}
}
源码 【进阶 | AsyncTask 源码浅析】两个线程池:
SerialExecutor
:任务排队THREAD_POOL_EXECUTOR
:执行任务
-
AsyncTask
的类加载必须在主线程中完成,因为AsyncTask
类中有静态域sHandler
,用于切换主线程和子线程。sHnadler
必须获取主线程的Looper
,所以sHandler
必须在主线程中创建。而静态变量的创建在类加载的时候完成,所以AsyncTask
必须在主线程中加载。Android4.1之后系统自动完成这个操作。
- AsyncTask对象创建和
execute
方法必须在主线程中调用
文章图片
但是为啥呢?大都数帖子都说,因为sHandler必须在主线程中创建,但是sHandler为静态变量,在类加载的时候就创建了,而不是在对象生成的时候创建。这个解释是错误的。如果您知道为啥,还请指点一二。
- 一个
AsyncTask
只能被执行一次,不然会被报异常。查看源码,在执行executor()
方法之后,首先会检测AsyncTask的Status,如果 Status为RUNDING
或者FINISHED
则会抛出异常。在检测完Status
之后将其设置为RUNNING
。
public final AsyncTask 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;
}
Android3.0
之后AsyncTask
任务由一个线程串行执行,如果是调用executeOnExecute则是并行执行任务。查看源码
private static class SerialExecutor implements Executor {
final ArrayDeque mTasks = new ArrayDeque();
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);
// 执行任务
}
}
}
初始状态mActive为null,调用
scheduleNext()
,出队列让THREAD_POOL_EXECUTOR
线程池去执行任务。r.run()
执行完成之后运行finaly{}
语句,调用下一个任务,依次继续。参考资料
- 《Android艺术探索》
- android中handler的一些总结以及使用(三)之HandleThread的使用
推荐阅读
- Android事件传递源码分析
- 推荐系统论文进阶|CTR预估 论文精读(十一)--Deep Interest Evolution Network(DIEN)
- Quartz|Quartz 源码解析(四) —— QuartzScheduler和Listener事件监听
- [源码解析]|[源码解析] NVIDIA HugeCTR,GPU版本参数服务器---(3)
- ffmpeg源码分析01(结构体)
- 普通人进阶的三个过程-很多人都知道,但却本末倒置
- 易效能进阶课程笔记29
- Java程序员阅读源码的小技巧,原来大牛都是这样读的,赶紧看看!
- Vue源码分析—响应式原理(二)
- SwiftUI|SwiftUI iOS 瀑布流组件之仿CollectionView不规则图文混合(教程含源码)