ANR与OOM

ANR (Application Not Responding),意思是应用没有响应。

在如下情况下,Android会报出ANR错误:

1主线程 (事件处理线程” UI线程5秒内没有响应输入事件

2BroadcastReceiver 没有在10秒内完成返回

通常情况下,下面这些做法会导致ANR 

1、在主线程内进行网络操作

2、在主线程内进行一些缓慢的磁盘操作(例如执行没有优化过的SQL查询)

应用应该在5秒或者10秒内响应,否则用户会觉得这个应用很垃圾”“”“”…等等

逻辑应该是
1. new出一个新的线程,进行数据请求;
2. 获取数据后,调用handler.sendMessage方法;

3. handlerhandle()方法中更新UI。

 

 

考虑上面的ANR定义,让我们来研究一下为什么它会在Android应用程序里发生和如何最佳构建应用程序来避免ANR

Android应用程序通常是运行在一个单独的线程(例如,main)里。这意味着你的应用程序所做的事情如果在主线程里占用了太长的时间的话,就会引发ANR对话框,因为你的应用程序并没有给自己机会来处理输入事件或者Intent广播。

因此,运行在主线程里的任何方法都尽可能少做事情。特别是,Activity应该在它的关键生命周期方法(如onCreate()onResume())里尽可能少的去做创建操作。潜在的耗时操作,例如网络或数据库操作,或者高耗时的计算如改变位图尺寸,应该在子线程里(或者以数据库操作为例,通过异步请求的方式)来完成。然而,不是说你的主线程阻塞在那里等待子线程的完成——也不是调用Thread.wait()或是Thread.sleep()。替代的方法是,主线程应该为子线程提供一个Handler,以便完成时能够提交给主线程。以这种方式设计你的应用程序,将能保证你的主线程保持对输入的响应性并能避免由于5秒输入事件的超时引发的ANR对话框。这种做法应该在其它显示UI的线程里效仿,因为它们都受相同的超时影响。

 

IntentReceiver执行时间的特殊限制意味着它应该做:在后台里做小的、琐碎的工作如保存设定或者注册一个Notification。和在主线程里调用的其它方法一样,应用程序应该避免在BroadcastReceiver里做耗时的操作或计算。但不再是在子线程里做这些任务(因为BroadcastReceiver的生命周期短),替代的是,如果响应Intent广播需要执行一个耗时的动作的话,应用程序应该启动一个Service。顺便提及一句,你也应该避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广播时需要向用户展示什么,你应该使用Notification Manager

来实现。

 

OOM:

        为了能够使Android应用程序能够高效快速地运行,所以Android的每个应用程序都会用一个专有的Davilk虚拟机实例对象来运行,这个Davilk对象是由Zygote服务进程孵化出来的,这样的机制使每个应用进程都只能在属于自己的进程空间中运行。Android为不同类型的进程分配了不同的内存使用上限,也就是设置了一个阈值,当这个上限被超过时,就视为OOM,通过一定的选择策略,某些进程就会被系统kill掉以释放内存。

在编程中有些现象很容易造成造成内存泄露,我们应该尽量避免:
1.引用没释放造成的内存泄露
2.资源对象没关闭造成的内存泄露(比如没有关闭访问文件的输入输出流)
3.Bitmap没调用recycle(Bitmap对象在不使用时,我们应该先调用recycle()释放内存,然后才它设置为null.)
4.构造Adapter时,没有使用缓存的 convertView
5.一些不良代码成内存压力(一些代码虽然不会直接造成内存泄露,但是它一直占用内存就会影响其他进程访问内存,间接造成OOM)

 

 

补充:大量读取图片,比如listview或者相册,读取网络图片时,使用软引用(SoftReference),让系统能够自动的利用内存空间。


查找内存泄露的方法:

        与C++的内存不同,C++的内存泄露是由于分配了内存给某程序但是又没有回收造成的。Java的内存泄露则是引用了一些垃圾对象,意思就是说程序引用了某些对象,但是又从来没有使用过。

Jave中的引用分为3种:

强引用:引用为空的时候,Java的垃圾回收器会处理。一般来说自己写的程序大部分都是强引用。

软引用:堆内存不够的时候,Java的垃圾回收器会处理这类引用。

弱引用:Jave的垃圾回收器每次都会回收这类引用。

如何用MAT来分析,前提是Android开发和测试的工具安装完整,SDKEclipse

1.打开Eclipse

2.选择 Help->Install New Software;

3.Work with中添加站点:http://download.eclipse.org/mat/1.0/update-site/(这个地址可能会变化,但是新的地址可以在官方网站上找到:http://www.eclipse.org/mat/downloads.php )

4.生成.hprof文件:插入SD卡(Android机器很多程序都需要插入SD卡),并将设备连接到PC,在Eclipse中的DDMS中选择要测试的进程,然后点击Update Heap Dump HPROF file两个Button.hprof 文件会自动保存在SD卡上,把 .hprof 文件拷贝到PC上的\ android-sdk-windows\tools目录下。这个由DDMS生成的文件不能直接在MAT打开,需要转换。运行cmd打开命令行,cd\ android-sdk-windows\tools所在目录,并输入命令hprof-conv xxxxx.hprof yyyyy.hprof,其中xxxxx.hprof为原始文件,yyyyy.hprof为转换过后的文件。转换过后的文件自动放在android-sdk-windows\tools 目录下。OK,到此为止,.hprof文件处理完毕,可以用来分析内存泄露情况了。

5.打开MAT

Eclipse中点击Windows->Open Perspective->Other->Memory Analysis

6.导入.hprof文件

MAT中点击 File->Open File,浏览到刚刚转换而得到的.hprof文件,并Cancel掉自动生成报告,点击Dominator Tree,并按Package分组,选择自己所定义的Package 类点右键,在弹出菜单中选择List objects->With incoming references

这时会列出所有可疑类,右键点击某一项,并选择Path to GC Roots->exclude weak/soft references,会进一步筛选出跟程序相关的所有有内存泄露的类。据此,可以追踪到代码中的某一个产生泄露的类。

 

 

 

posted @ 2013-06-25 22:10  tlonge  阅读(1398)  评论(0编辑  收藏  举报