多线程之 AsyncTask 使用详解和从源码中深入理解 AsyncTask 机制
前言
AsyncTask是Android提供的一个助手类,它对Thread和Handler进行了封装,方便我们使用;
Android之所以提供AsyncTask这个类,就是为了方便我们在后台线程中执行操作,然后将结果发送给主线程,从而在主线程中进行UI更新等操作。在使用AsyncTask时,我们无需关注Thread和Handler,AsyncTask内部会对其进行管理,这样我们就只需要关注于我们的业务逻辑即可;
一、AsyncTask介绍和使用
1、AsyncTask介绍
AsyncTask有四个重要的回调方法,分别是:onPreExecute、doInBackground, onProgressUpdate 和 onPostExecute。这四个方法会在AsyncTask的不同时期进行自动调用,我们只需要实现这几个方法的内部逻辑即可;
这四个方法的一些参数和返回值都是基于泛型的,而且泛型的类型还不一样,所以在AsyncTask的使用中会遇到三种泛型参数:Params, Progress 和 Result;
public abstract class AsyncTask<Params, Progress, Result>
AsyncTask是一个抽象泛型类。
其中,三个泛型类型参数的含义如下:
Params:开始异步任务执行时传入的参数类型;
Progress:异步任务执行过程中,返回下载进度值的类型;
Result:异步任务执行完成后,返回的结果类型
2、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(); } } }
模拟了一个下载任务,在doInBackground()方法中去执行具体的下载逻辑,在onProgressUpdate()方法中显示当前的下载进度,在onPostExecute()方法中来提示任务的执行结果。如果想要启动这个任务,只需要简单地调用以下代码即可:
new DownloadTask().execute();
3、使用AsyncTask的注意事项
①异步任务的实例必须在UI线程中创建,即AsyncTask对象必须在UI线程中创建;
②execute(Params… params)方法必须在UI线程中调用;
③不要手动调用onPreExecute(),doInBackground(Params… params),onProgressUpdate(Progress… values),onPostExecute(Result result)这几个方法;
④不能在doInBackground(Params… params)中更改UI组件的信息;
⑤一个任务实例只能执行一次,如果执行第二次将会抛出异常;
二、AsyncTask的源码分析
1、AsyncTask的构造函数
/*** 具体使用*/MyTask mTask = new MyTask();/*** 源码分析:AsyncTask的构造函数*/public AsyncTask() {// 初始化WorkerRunnable变量 = 一个可存储参数的Callable对象 mWorker = new WorkerRunnable<Params, Result>() {// 在任务执行线程池中回调:THREAD_POOL_EXECUTOR.execute()// 下面会详细讲解public Result call() throws Exception {// 添加线程的调用标识mTaskInvoked.set(true); Result result = null;try {// 设置线程的优先级Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);// 执行异步操作 = 耗时操作// 即 我们使用过程中复写的耗时任务result = doInBackground(mParams);Binder.flushPendingCommands();} catch (Throwable tr) {mCancelled.set(true);// 若运行异常,设置取消的标志throw tr;} finally {// 把异步操作执行的结果发送到主线程// 从而更新UI,下面会详细讲解postResult(result); }return result;}};// 初始化FutureTask变量 = 1个FutureTask mFuture = new FutureTask<Result>(mWorker) {// done()简介:FutureTask内的Callable执行完后的调用方法// 作用:复查任务的调用、将未被调用的任务的结果通过InternalHandler传递到UI线程@Overrideprotected void done() {try {// 在执行完任务后检查,将没被调用的Result也一并发出 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) {//若 发生异常,则将发出nullpostResultIfNotInvoked(null);}}};}/**WorkerRunnable类的构造函数*/private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {// 此处的Callable也是任务;// 与Runnable的区别:Callable<T>存在返回值 = 其泛型Params[] mParams;}/*** 分析:FutureTask类的构造函数* 定义:1个包装任务的包装类* 注:内部包含Callable<T> 、增加了一些状态标识 & 操作Callable<T>的接口*/public FutureTask(Callable<V> callable) {if (callable == null)throw new NullPointerException();this.callable = callable;this.state = NEW; }// 回到调用原处/*** postResultIfNotInvoked()*/private void postResultIfNotInvoked()(Result result) {// 取得任务标记final boolean wasTaskInvoked = mTaskInvoked.get();// 若任务无被执行,将未被调用的任务的结果通过InternalHandler传递到UI线程if (!wasTaskInvoked) {postResult(result);}}
创建了1个WorkerRunnable类 的实例对象 & 复写了call()方法;
创建了1个FutureTask类 的实例对象 & 复写了 done();
2、execute(Params… params)
/*** 具体使用*/mTask.execute();/*** 源码分析:AsyncTask的execute()*/public final AsyncTask<Params, Progress, Result> execute(Params... params) {return executeOnExecutor(sDefaultExecutor, params);// ->>分析1}/*** executeOnExecutor(sDefaultExecutor, params)* 参数说明:sDefaultExecutor = 任务队列 线程池类(SerialExecutor)的对象*/public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {// 判断 AsyncTask 当前的执行状态// PENDING = 初始化状态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)");}}// 将AsyncTask状态设置为RUNNING状态mStatus = Status.RUNNING;// 主线程初始化工作onPreExecute();// 添加参数到任务中mWorker.mParams = params;// 执行任务// 此处的exec = sDefaultExecutor = 任务队列 线程池类(SerialExecutor)的对象exec.execute(mFuture);return this;}/*** exec.execute(mFuture)* 说明:属于任务队列 线程池类(SerialExecutor)的方法*/private static class SerialExecutor implements Executor {// SerialExecutor = 静态内部类// 即 是所有实例化的AsyncTask对象公有的// SerialExecutor 内部维持了1个双向队列;// 容量根据元素数量调节final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();Runnable mActive;// execute()被同步锁synchronized修饰// 即说明:通过锁使得该队列保证AsyncTask中的任务是串行执行的// 即 多个任务需1个个加到该队列中;然后 执行完队列头部的再执行下一个,以此类推public synchronized void execute(final Runnable r) {// 将实例化后的FutureTask类 的实例对象传入// 即相当于:向队列中加入一个新的任务mTasks.offer(new Runnable() {public void run() {try {r.run();} finally {scheduleNext();}}});// 若当前无任务执行,则去队列中取出1个执行if (mActive == null) {scheduleNext();}}// protected synchronized void scheduleNext() {// 取出队列头部任务if ((mActive = mTasks.poll()) != null) {// 执行取出的队列头部任务// 即调用执行任务线程池类(THREAD_POOL_EXECUTOR)THREAD_POOL_EXECUTOR.execute(mActive);}}}
执行任务前,通过 任务队列 线程池类(SerialExecutor)将任务按顺序放入到队列中;
通过同步锁修饰execute()从而保证AsyncTask中的任务是串行执行的;
之后的线程任务执行是 通过任务线程池类(THREAD_POOL_EXECUTOR) 进行的;
3、THREAD_POOL_EXECUTOR.execute()
/*** 源码分析:THREAD_POOL_EXECUTOR.execute()* 说明:* a. THREAD_POOL_EXECUTOR实际上是1个已配置好的可执行并行任务的线程池* b. 调用THREAD_POOL_EXECUTOR.execute()实际上是调用线程池的execute()去执行具体耗时任务* c. 而该耗时任务则是步骤2中初始化WorkerRunnable实例对象时复写的call()* 注:下面先看任务执行线程池的线程配置过程,看完后请回到步骤2中的源码分析call()*/// 参数设置//获得当前CPU的核心数private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//设置线程池的核心线程数2-4之间,但是取决于CPU核数private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));//设置线程池的最大线程数为 CPU核数*2+1private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;//设置线程池空闲线程存活时间30sprivate static final int KEEP_ALIVE_SECONDS = 30;//初始化线程工厂private static final ThreadFactory sThreadFactory = new ThreadFactory() {private final AtomicInteger mCount = new AtomicInteger(1);public Thread newThread(Runnable r) {return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());}};//初始化存储任务的队列为LinkedBlockingQueue 最大容量为128private static final BlockingQueue<Runnable> sPoolWorkQueue =new LinkedBlockingQueue<Runnable>(128);// 根据参数配置执行任务线程池,即 THREAD_POOL_EXECUTORpublic static final Executor THREAD_POOL_EXECUTOR;static {ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,sPoolWorkQueue, sThreadFactory);// 设置核心线程池的 超时时间也为30sthreadPoolExecutor.allowCoreThreadTimeOut(true);THREAD_POOL_EXECUTOR = threadPoolExecutor;}4、call()/*** AsyncTask的构造函数*/public AsyncTask() {// 初始化WorkerRunnable变量 = 一个可存储参数的Callable对象mWorker = new WorkerRunnable<Params, Result>() {public Result call() throws Exception {// 添加线程的调用标识mTaskInvoked.set(true); Result result = null;try {// 设置线程的优先级Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);// 执行异步操作 = 耗时操作// 即 我们使用过程中复写的耗时任务result = doInBackground(mParams);Binder.flushPendingCommands();} catch (Throwable tr) {mCancelled.set(true);// 若运行异常,设置取消的标志throw tr;} finally {// 把异步操作执行的结果发送到主线程// 从而更新UI ->>分析1postResult(result); }return result;}};}/*** postResult(result)*/private Result postResult(Result result) {@SuppressWarnings("unchecked")// 创建Handler对象 ->> 源自InternalHandler类Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,new AsyncTaskResult<Result>(this, result));// 发送消息到Handler中message.sendToTarget();return result;}/*** InternalHandler类*/private static class InternalHandler extends Handler {// 构造函数public InternalHandler() {super(Looper.getMainLooper());// 获取的是主线程的Looper()// 故 AsyncTask的实例创建 & execute()必须在主线程使用}@Overridepublic void handleMessage(Message msg) {AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;switch (msg.what) {// 若收到的消息 = MESSAGE_POST_RESULT// 则通过finish() 将结果通过Handler传递到主线程case MESSAGE_POST_RESULT:result.mTask.finish(result.mData[0]); ->>分析3break;// 若收到的消息 = MESSAGE_POST_PROGRESS// 则回调onProgressUpdate()通知主线程更新进度的操作case MESSAGE_POST_PROGRESS:result.mTask.onProgressUpdate(result.mData);break;}}}/*** result.mTask.finish(result.mData[0])*/private void finish(Result result) {// 先判断是否调用了Cancelled()// 若调用了则执行我们复写的onCancelled()// 即 取消任务时的操作if (isCancelled()) {onCancelled(result);} else {// 若无调用Cancelled(),则执行我们复写的onPostExecute(result)// 即更新UI操作onPostExecute(result);}// 注:不管AsyncTask是否被取消,都会将AsyncTask的状态变更为:FINISHEDmStatus = Status.FINISHED;}
任务线程池类(THREAD_POOL_EXECUTOR)实际上是1个已配置好的可执行并行任务的线程池;
调用THREAD_POOL_EXECUTOR.execute()实际上是调用线-程池的execute()去执行具体耗时任务;
而该耗时任务则是步骤2中初始化 WorkerRunnable实例对象时复写的call()内容;
在call()方法里,先调用 我们复写的doInBackground(mParams)执行耗时操作;
再调用postResult(result), 通过 InternalHandler 类 将任务消息传递到主线程;根据消息标识(MESSAGE_POST_RESULT)判断,最终通过finish()调用我们复写的onPostExecute(result),从而实现UI更新操作
AsyncTask的源码 分析完毕;
总结
生命周期:AsyncTask不与任何组件绑定生命周期,所以在Activity/或者Fragment中创建执行AsyncTask时,最好在Activity/Fragment的onDestory()调用 cancel(boolean);
内存泄漏:如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露;
结果丢失:屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask(非静态的内部类)会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效;
多线程之 AsyncTask 使用详解和从源码中深入理解 AsyncTask 机制相关推荐
- android WebView详解,常见漏洞详解和安全源码(下)
上篇博客主要分析了 WebView 的详细使用,这篇来分析 WebView 的常见漏洞和使用的坑. 上篇:android WebView详解,常见漏洞详解和安全源码(上) 转载请注明出处:http ...
- android WebView详解,常见漏洞详解和安全源码(上)
这篇博客主要来介绍 WebView 的相关使用方法,常见的几个漏洞,开发中可能遇到的坑和最后解决相应漏洞的源码,以及针对该源码的解析. 由于博客内容长度,这次将分为上下两篇,上篇详解 WebView ...
- FPGA学习之路—接口(3)—SPI详解及Verilog源码分析
FPGA学习之路--SPI详解及Verilog源码分析 概述 SPI = Serial Peripheral Interface,是串行外围设备接口,是一种高速,全双工,同步的通信总线. 优点 支持全 ...
- faster rcnn fpn_Faster-RCNN详解和torchvision源码解读(三):特征提取
我们使用ResNet-50-FPN提取特征 model = torchvision.models.detection.fasterrcnn_resnet50_fpn(pretrained=True) ...
- 【Python】基金/股票 最大回撤率计算与绘图详解(附源码和数据)
如果你想找的是求最大回撤的算法,请跳转:[Python] 使用动态规划求解最大回撤详解 [Python]基金/股票 最大回撤率计算与绘图详解(附源码和数据) 0. 起因 1. 大成沪深300指数A 5 ...
- 生成对抗网络入门详解及TensorFlow源码实现--深度学习笔记
生成对抗网络入门详解及TensorFlow源码实现–深度学习笔记 一.生成对抗网络(GANs) 生成对抗网络是一种生成模型(Generative Model),其背后最基本的思想就是从训练库里获取很多 ...
- CNN入门详解及TensorFlow源码实现--深度学习笔记
CNN入门详解及TensorFlow源码实现–深度学习笔记 ##一.卷积神经网络 ###1.简介 卷积神经网络是一种前馈神经网络,它的人工神经元可以响应一部分覆盖范围内的周围单元,对于大型图像处理有出 ...
- EKF SLAM Matlab仿真实践详解(附源码)
EKF SLAM Matlab仿真实践详解(附源码) 为提供更好的阅读体验,详细内容及源码请移步https://github.com/Nrusher/EKF_SLAM 或 https://gitee. ...
- aes js加密php解密实例,基于PHP和JS的AES相互加密解密方法详解(CryptoJS)_PHP_JS_AES源码...
[实例简介] 基于PHP和JS的AES相互加密解密方法详解(CryptoJS)_PHP_JS_AES源码 [实例截图] [核心代码] 基于PHP和JS的AES相互加密解密方法详解(CryptoJS)_ ...
最新文章
- python编程语法-Python编程入门——基础语法详解(经典)
- java8 lambda 接口_Java8新特性之一:Lambda表达式
- 为了上班摸鱼,我用Python开发“BOSS来了”!
- 前端学习(2267)vue造轮子之添加icon
- 《信息安全系统设计基础》实验四报告
- c++ 未定义标识符string_Redis之String的数据结构
- 空降新书榜,霸占前三甲,还有什么是这些书做不到的?!
- 数学建模学习(24): 排队论模型完整详细讲解,数学与案例结合,lingo软件搭配,数学不好也能学会!
- SOP封装的后缀字母L M N都代表什么意思?
- Unity官方文档(英文)
- 大数据产品开发流程规范_大数据架构流程图
- python编写摇骰子游戏_python摇骰子猜大小的小游戏
- 操作系统----校招笔试面试常考内容总结
- 项目管理——时间、成本、范围的三重约束
- 对Autorun.inf类U盘病毒的攻防经验总结
- homelede软路由设置方法_二级lede软路由设置方法,lede设置软路由网卡
- 真实吐槽点评:华为nova8SE和华为nova7Pro区别-哪个更值得入手-参数对比
- 发现网站被劫持该怎么办?网站域名劫持的情况及解决办法
- 企业资金链断裂如何表现在现金流量表中?看净现金流量吗?
- 408计算机组成原理历年真题