Android线程管理之AsyncTask异步任务
前言:
前面几篇文章主要学习了线程以及线程池的创建与使用,今天来学习一下AsyncTask异步任务,学习下AsyncTask到底解决了什么问题?然而它有什么弊端?正所谓知己知彼百战百胜嘛!
线程管理相关文章地址:
- Android线程管理之Thread使用总结
- Android线程管理之ExecutorService线程池
- Android线程管理之ThreadPoolExecutor自定义线程池
- Android线程管理之AsyncTask异步任务
- Android线程管理之ThreadLocal理解及应用场景
产生背景:
我们都知道Android应用程序是单线程模型,在子线程无法直接操作UI主线程,必须通过Handler机制,想了解这方面的知识可以参考这篇文章:Android消息传递之Handler消息机制(一),所以基于这种考虑所以我们一般情况会采用Thread+Handler来处理比较耗时的操作,但是我们都知道每次new Thread()开销比较大,而且缺乏管理,被称为野线程,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪,不利于扩展,比如如定时执行、定期执行、线程中断,这时我们引入了线程池的概念,整个解决问题的模型就变成了Runnable+Executor+Handler,为了降低开发者的开发难度,AsyncTask应运而生,AsyncTask是对线程池的一个封装,使用其自定义的 Executor 来调度线程的执行方式(并发还是串行),并使用 Handler 来完成子线程和主线程数据的共享。
AsyncTask介绍
AsyncTask是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度,最后反馈执行的结果给UI主线程.
AsyncTask主要参数、函数解析
1.)AsyncTask是抽象类.AsyncTask定义了三种泛型类型 Params,Progress和Result
- Params 启动任务执行的输入参数,比如下载URL
- Progress 后台任务执行的百分比,比如下载进度
- Result 后台执行任务最终返回的结果,比如下载结果
2.)继承AsyncTask可以实现的函数
- onPreExecute()//此函数是在任务没被线程池执行之前调用 运行在UI线程中 比如现在一个等待下载进度Progress,也可以不用实现
- doInBackground(Params... params)//此函数是在任务被线程池执行时调用 运行在子线程中,在此处理比较耗时的操作 比如执行下载,此函数是抽象函数必须实现
- onProgressUpdate(Progress... values)//此函数是任务在线程池中执行处于Running状态,回调给UI主线程的进度 比如上传或者下载进度,也可以不用实现
- onPostExecute(Result result)//此函数任务在线程池中执行结束了,回调给UI主线程的结果 比如下载结果,也可以不用实现
- onCancelled(Result result)/onCancelled()//此函数表示任务关闭
3.)AsyncTask主要公共函数
- cancel (boolean mayInterruptIfRunning)//尝试取消这个任务的执行,如果这个任务已经结束或者已经取消或者不能被取消或者某些其他原因,那么将导致这个操作失败,当调用此方法时,此方法执行成功并且这个任务还没有执行,那么此任务将不再执行。如果任务已经开始,这时执行此操作传入的参数mayInterruptIfRunning为true,执行此任务的线程将尝试中断该任务
- execute (Params... params)//用指定的参数来执行此任务,这个方法将会返回此任务本身,所以调用者可以拥有此任务的引用。此方法必须在UI线程中调用
- executeOnExecutor(Executor exec,Params... params)//用指定的参数,运行在指定的线程池中,这个方法将会返回此任务本身,所以调用者可以拥有此任务的引用。此方法必须在UI线程中调用
- get ()//等待计算结束并返回结果
- get (long timeout, TimeUnit unit)//等待计算结束并返回结果,最长等待时间为:timeOut(超时时间)
- getStatus ()//获得任务的当前状态 PENDING(等待执行)、RUNNING(正在运行)、FINISHED(运行完成)
- isCancelled ()//如果在任务正常结束之前取消任务成功则返回true,否则返回false
AsyncTask示例:
1.) 模拟一个下载文件的需求
String url = "www.xxx.jpg"; AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String, Integer, String>() { @Override protected void onPreExecute() {//此函数是在任务没被线程池执行之前调用 运行在UI线程中 比如现在一个等待下载进度Progress super.onPreExecute(); Log.e(TAG, "AsyncTask onPreExecute"); } @Override protected String doInBackground(String[] params) {//此函数是在任务被线程池执行时调用 运行在子线程中,在此处理比较耗时的操作 比如执行下载 String url = params[0]; Log.e(TAG, "AsyncTask doInBackground url---->" + url); //模拟下载 int i = 0; for (i = 20; i <= 100; i += 20) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Log.e(TAG, "AsyncTask doInBackground result--progress-->" + i); publishProgress(i); } String result = "download end"; return result; } @Override protected void onProgressUpdate(Integer... values) {//此函数是任务在线程池中执行处于Running状态,回调给UI主线程的进度 比如上传或者下载进度 super.onProgressUpdate(values); int progress = values[0]; Log.e(TAG, "AsyncTask onProgressUpdate progress---->" + progress); } @Override protected void onPostExecute(String s) {//此函数任务在线程池中执行结束了,回调给UI主线程的结果 比如下载结果 super.onPostExecute(s); Log.e(TAG, "AsyncTask onPostExecute result---->" + s); } @Override protected void onCancelled() {//此函数表示任务关闭 super.onCancelled(); Log.e(TAG, "AsyncTask onCancelled"); } @Override protected void onCancelled(String s) {//此函数表示任务关闭 返回执行结果 有可能为null super.onCancelled(s); Log.e(TAG, "AsyncTask onCancelled---->" + s); } }; asyncTask.execute(url);
运行结果
2.)如何关闭一个AsyncTask
boolean mayInterruptIfRunning传true的情况还是传false的情况
if (!asyncTask.isCancelled()) { boolean isCancel = asyncTask.cancel(true); Log.e(TAG, "AsyncTask isCancel---->" + isCancel); }
运行结果:测试发现运行结果一样
通过上面运行结果可以看出,无论mayInterruptIfRunning传入true或者false运行的结果都一样,也就是说当我们调用cancel (boolean mayInterruptIfRunning)函数之后,在doInBackground()return后 ,我们将会调用onCancelled(Object) 不在调用onPostExecute(Object),但是根据运行结果看,我们通过这个函数并没有真正的终止子线程继续运行,只是舍弃了运行结果,AsyncTask不会不考虑结果而直接结束一个线程。调用cancel()其实是给AsyncTask设置一个"canceled"状态。这取决于你去检查AsyncTask是否已经取消,之后决定是否终止你的操作。对于mayInterruptIfRunning——它所作的只是向运行中的线程发出interrupt()调用。在这种情况下,你的线程是不可中断的,也就不会终止该线程,我们可以在doInBackground(Params... params)中定期检查isCancelled()状态,如果检查到已经关闭,直接终止耗时操作。比如上面的下载可以改成
@Override protected String doInBackground(String[] params) {//此函数是在任务被线程池执行时调用 运行在子线程中,在此处理比较耗时的操作 比如执行下载 String url = params[0]; Log.e(TAG, "AsyncTask doInBackground url---->" + url); //模拟下载 int i = 0; for (i = 20; i <= 100; i += 20) { if (isCancelled()) { break; } try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Log.e(TAG, "AsyncTask doInBackground result--progress-->" + i); publishProgress(i); } if (isCancelled()) { return "download cancel"; } String result = "download end"; return result; }
运行结果
AsyncTask需要注意的地方:
1.)生命周期
AsyncTask不与任何组件绑定生命周期,所以在Activity/或者Fragment中创建执行AsyncTask时,最好在Activity/Fragment的onDestory()调用 cancel(boolean);
2.)内存泄漏
如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄 露。
3.) 结果丢失
屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。
4.)并行还是串行
在Android 1.6之前的版本,AsyncTask是串行的,在1.6至2.3的版本,改成了并行的。在2.3之后的版本又做了修改,可以支持并行和串行,当想要串行执行时,直接执行execute()方法,如果需要并行执行,则要执行executeOnExecutor(Executor)