AsyncTask的正确使用
来源:互联网 发布:淘宝店铺红包在哪里看 编辑:程序博客网 时间:2024/04/29 22:42
首先,附上AsyncTask使用时的缺陷问题:
出处:
http://blog.csdn.net/goodlixueyong/article/details/45895997
原文内容:
在Android开发中,AsyncTask可以使得用户避免直接使用Thread类和Handler 来处理后台操作,适用于需要异步处理数据并将数据更新到界面上的情况。AsyncTask适用于后台操作只有几秒的短时操作。但是AsyncTask本身存在很多糟糕的问题,如果使用中不注意,将会影响程序的健壮性。
1、生命周期
很多开发者会认为一个在Activity中创建的AsyncTask会随着Activity的销毁而销毁。然而事实并非如此。AsyncTask会一直执行, 直到doInBackground()方法执行完毕。然后,如果 cancel(boolean)被调用, 那么onCancelled(Result result) 方法会被执行;否则,执行onPostExecute(Result result) 方法。如果我们的Activity销毁之前,没有取消 AsyncTask,这有可能让我们的AsyncTask崩溃(crash)。因为它想要处理的view已经不存在了。所以,我们总是必须确保在销毁活动之前取消任务。总之,我们使用AsyncTask需要确保AsyncTask正确地取消。
另外,即使我们正确地调用了cancle() 也未必能真正地取消任务。因为如果在doInBackgroud里有一个不可中断的操作,比如BitmapFactory.decodeStream(),那么这个操作会继续下去。
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)
------------------------------------------------------------------------------------------------------------------------------
目前项目中使用了大量的异步任务(AsyncTask)来处理后台接口的数据(执行网络操作)。有时候网络比较慢或者执行长时间的网络的时候(长时网络不建议使用AsyncTask),用户返回当前的Activity,但是doInBackground方法还在执行,如果我们不使用静态内部类的方式来创建AsyncTask的话,这个task会一直持有activity的引用,这就很可能会造成内存泄露,并且当doInBackground方法执行完成,调用onPostExecute去更新UI的时候,更有可能出现异常(使用了已经不存在的View)。
所以我们应该正确的关闭task,并且防止引用activity引起内存泄露。
关于如何关闭Task:
AsyncTask为我们提供了cancel方法,但是只执行cancel是不能够完全停止任务的,特别是doInBackground方法中在执行网络操作,或是线程在执行sleep循环操作的时候,这个时候是打断不了这些操作的。那么cancle到底有什么用呢?
我们来看下如下代码:
private AsyncTask task; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* 用匿名内部类的方式创建*/ task = new AsyncTask() { @Override protected Object doInBackground(Object[] params) { for(int i=0;i<10;i++){ Log.i("task","i="+i); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } if(isCancelled()){ break; } } return null; } @Override protected void onPostExecute(Object o) { super.onPostExecute(o); Log.i("task","执行结束了"); } @Override protected void onCancelled() { super.onCancelled(); Log.i("task","执行了取消"); } }; task.execute(); } @Override protected void onDestroy() { super.onDestroy(); task.cancel(true); }我在activity中简单的创建了一个异步任务(注意这里使用了匿名内部类来创建,这样创建会导致task持有当前activity的引用),doInBackground中执行一个循环计数的操作,每次计数睡眠2s(这里可以看做是一个网络请求的场景),当我把
task.cancel(true);注释掉后,这个任务该怎么执行还是怎么执行,按返回键退出当前activity后,这个循环计数还在后台继续执行,当执行完成后会调用onPostExecute方法(可看打印结果)
当去掉
task.cancel(true);的注释,把
if(isCancelled()){ break; }注释掉。虽然在退出的时候执行了cancel,但是doInBackground里的循环还是继续在执行,但是当执行完成的时候不会再调用onPostExecute方法了,而是调用了onCancelled方法(可看打印结果)
把注释全部取消,我们在doInBackground的for循环体里面做了isCancelled判断,当退出的时候调用task.cancel方法的时候,我们进入这个条件,通过这个条件判断可以退出循环,结束任务,最后调用onCancelled方法。
对于这样的一个场景,我们确实可以通过执行task的cancel方法,通过判断isCancelled值来退出任务,在activity结束的时候就结束了任务。但是相对于其他情况(比如说操作网络的时候),doInBackground方法里面还是会继续执行,直到结束。但是至少,我们使用了cancel方法,最终不会执行onPostExecute,而改去执行
onCancelled我们肯定不会在这个方法里面去操作UI的啦。这样就避免了一些异常的发生。
于是相对于不能中断的任务,为避免任务一直持有activity,我们可以通过使用静态内部类,并且使用WeakReference来包裹我们的activity以达到更新UI的目的
如下代码:
static class MyTask extends AsyncTask<String,Integer,String>{ private WeakReference<Activity> weakAty; public MyTask(Activity activity){ weakAty = new WeakReference<Activity>(activity); } @Override protected String doInBackground(String... params) { for(int i=0;i<10;i++){ Log.i("Mytask","i="+i); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } if(isCancelled()){ break; } } return null; } @Override protected void onPostExecute(String s) { super.onPostExecute(s); Log.i("Mytask","执行结束了"); AsyncTaskActivity mActivity; if((mActivity= (AsyncTaskActivity) weakAty.get())!=null){ mActivity.doSomething(); } } @Override protected void onCancelled() { super.onCancelled(); Log.i("Mytask","执行了取消"); } }
附上完整代码:
public class AsyncTaskActivity extends Activity { private AsyncTask task; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* 用匿名内部类的方式创建*/ task = new AsyncTask() { @Override protected Object doInBackground(Object[] params) { for(int i=0;i<10;i++){ Log.i("task","i="+i); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } if(isCancelled()){ break; } } return null; } @Override protected void onPostExecute(Object o) { super.onPostExecute(o); Log.i("task","执行结束了"); } @Override protected void onCancelled() { super.onCancelled(); Log.i("task","执行了取消"); } }; task.execute(); //从log可以看到,上个task和下面的task是串行执行的 new MyTask(this).execute(); } @Override protected void onDestroy() { super.onDestroy(); task.cancel(true); //这里不做取消MyTask的任务是为了验证weakReference是否起作用 } public void doSomething(){ //为保险,还是需要判断下当前activity是否已经销毁,因为weakReference修饰的对象并不是马上就能被回收 Log.i("AsyncActivity","异步任务完成,更新UI"); } static class MyTask extends AsyncTask<String,Integer,String>{ private WeakReference<Activity> weakAty; public MyTask(Activity activity){ weakAty = new WeakReference<Activity>(activity); } @Override protected String doInBackground(String... params) { for(int i=0;i<100;i++){ Log.i("Mytask","i="+i); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } if(isCancelled()){ break; } } return null; } @Override protected void onPostExecute(String s) { super.onPostExecute(s); Log.i("Mytask","执行结束了"); AsyncTaskActivity mActivity; if((mActivity= (AsyncTaskActivity) weakAty.get())!=null){ mActivity.doSomething(); } } @Override protected void onCancelled() { super.onCancelled(); Log.i("Mytask","执行了取消"); } }}
记录只为整理自己理解的,如果有不对,还请指正!
- AsyncTask的正确使用
- 使用AsyncTask防止Memory Leaks(内存泄漏)的正确姿势
- 关于正确使用Android AsyncTask学习整理
- AsyncTask的使用
- AsyncTask类的使用
- Android 的AsyncTask使用
- AsyncTask的基本使用
- AsyncTask的简单使用
- AsyncTask的简单使用
- Android AsyncTask的使用
- AsyncTask的使用
- AsyncTask的简单使用
- Android AsyncTask 的使用
- AsyncTask的使用
- AsyncTask类的使用
- AsyncTask的使用
- AsyncTask的使用步骤
- AsyncTask的使用
- Struts2图片的上传下载
- IT技术人的一点点思考
- JAVA+ffmpeg+mencoder转换视频
- Spring Boot使用自定义的properties
- 基于嵌入式操作系统的实时性多任务划分方法
- AsyncTask的正确使用
- 开发小结
- C# Socket网络编程TCP/IP层次模型、端口及报文等探讨
- 埋在我和极光推送之间的那些坑,好用的极光推送文档
- 共同学习Java源码--常用数据类型--String(十五)
- jQuery监听浏览器窗口大小的变化
- Docker集群(二) —— Docker资源管理
- c# winform实现dll加载时注册
- 一元多项式加法计算问题