Android异常与性能优化一
一、Anr异常
1、在主线程中进行了耗时操作。5s内没有响应用户的输入事件,如activity中,在主线程中做了超过10s的耗时操作,如service中
2、
3、
4、如何解决anr
如果thread不提高优先级,优先级和activity是一样的,仍然会出现anr
二、oom
1、
2、
、
内存溢出:当前占用的内存加上申请的内存超过了虚拟机的最大内存
内存抖动:短时间内,大量的对象被创建,然后又被马上释放,瞬间产生的对象会大量占用内存区域
内存泄漏:进程中的某些对象,它已经没有被其它地方引用到了,但是它们却可以直接或者间接地引用到gc roots,gc roots就是引用到其它还没有被引用到的对象,导致gc无法产生作用。内存泄漏严重到一定程度,会造成内存溢出,也就是oom的现象
3、
4、
①图片显示:加载合适尺寸的图片,显示缩略图的时候,不要调用网络请求加载大图
②及时释放内存:不定期地释放内存空间。bitmapfactory用来生成bitmap,bitmapfactory所有的生成方法都是调用jni来实现的,加载bitmap到内存区后是包含两部分,一部分是java区,一部分是c区,而bitmap是由java区来进行分配的,不用的时候会由java的回收机制回收掉。而对应的c部分的内存区域,虚拟机是不能直接回收的。、,只能调用底层功能来释放。释放内存,释放的就是c那部分内存。从源码中可以看到,recycle方法中确实调用了jni方法。如果不调用recycle,是否会造成内存泄漏,进而引起oom呢?其实 也不是,Android的每个应用都运行在独立的进程当中,它有一个独立的内存,如果进程被杀死了,内存也就被释放掉了,也包括c的那部分内存也会被释放掉
③图片压缩:刚开始需要加载一张很大很大的图片,这张图片有可能会超过内存大小,造成内存溢出。这时候就需要对加载的bitmap大小进行控制,也就是进行图片压缩。对bitmap进行压缩,需要用到bitmap的一个insamplesize的属性,就是把bitmap加载到内存之前,需要计算一个合适的缩放比例,避免不必要的大图载入
④inBitmap属性:使用bitmap的inBitmap的高级属性,可以提高A在ndroid系统执行和释放的执行效率,这个属性可以告诉bitmap的decode解码器在使用已经存在内存区域,而不是重新申请一块内存区域。意思就是说新decode的bitmap会使用之前那张bitmap在堆内存中占用的区域,就是说你这里就算是有成百上千张图片,它也只能使用屏幕能放下的图片的内存。这是Android系统对bitmap进行的优化。
⑤捕获异常:在读位图bitmap的时候,分给虚拟机堆栈内存的大小是有限制的,为了避免在分配内存的时候出现异常,在实例bitmap的时候一定要对异常进行捕获。捕获异常,捕获的都是exception,对于oom error来说,如果捕获异常是获取不到的,因为oom erro是一个error,是一个错误不是一个异常,
5、
三、bitmap
1、recycle
回收native内存和对内存中的内存。调用recycle方法后并不是直接清理内存,而是给gc发送一条通知,该bitmap会被标明死亡状态,不能再调用它以及其它的任何回调方法。
不必主动调用该方法,当bitmap没有被其它对象引用的时候,gc会主动回收该对象
2、lru:最近最少使用的缓存对象,会把它清除。内部有变量linkedhashmap,说明内部是由hashmap实现的。
内部使用了泛型类,并通过linkedhashmap来实现,同时它还提供了get、put方法来添加缓存对象的操作。最重要的就是trimtosize这个方法,这个方法会删除最少使用的和使用最久的缓存对象,并把新的缓存对象添加到缓存队列里面
3、计算insamplesize的值
4、缩略图:根据insamplesize的值计算相应的比例,保存到内存当中
5、三级缓存:所有的数据都需要和网络进行交互,包括图片。如果每次都进行请求,会很消耗流量,并且每次都请求,对用户体验特别不好。
三级缓存包括网络、本地、内存三级缓存。
原理:首次打开app,需要加载一些图片,肯定需要从网络上进行获取。当从网络上加载完图片之后,会把图片保存到sd卡和相应的内存当中各一份,这时请求同样url的一个bitmap的时候,就不用从网络上进行获取,此时直接从本地或者内存当中获取就可以了。这就是三级缓存的总体概念。网络缓存,加载缓慢,耗费流量。内存缓存读取速度最快。
四、ui卡顿
ui卡顿的主要原因来源于Android的渲染器做了太多的耗时操作,太多耗时操作的原因有可能是layout太复杂,有可能是ui上层叠了太多其它的layout,还有就是动画执行次数过多
1、Android系统每隔16毫秒发出信号,触发对ui进行渲染,如果每次渲染都成功,就能达到所需要的10fps,也就是每秒60帧。为了实现60fps,就说明程序的大多数操作都必须在16ms内完成,也就是拿1000/60,约等于16毫秒。当gc执行的时候,所有的线程都会暂停,只有当gc停止的时候,其它的线程才会继续执行。也就是说,在这16ms内进行渲染的时候,如果遇到了大量的gc操作,就会导致渲染时间不够,从而导致卡顿问题。大量的gc也会造成内存抖动。
2、overdraw:过度绘制。意思是在屏幕上,它的某一帧的像素在同一时间内被重复地绘制,经常出现在多层次的ui结果里面。如果有时候把ui设置成invisible或者gone的时候,也会进行ui的绘制操作,这就导致很多像素区域被绘制很多次。这样就浪费了cpu和gpu的资源。例如,activity的布局中有背景,viewgroup中有背景,view中有背景,这时仅仅通过移除非必须的背景图片,就能减少红色的overdraw区域,就能提升程序的性能,减少ui卡顿。
3、
4、
①布局优化:尽量使用include、merge标签。
尽量不存在冗余嵌套以及过于复杂的布局。
尽量使用gone替代invisible。因为invisible的时候,也是会绘制,影响性能的,但是设置为gone则不会再重新进行绘制。
尽量使用weight来减少运算。
布局中如果存在非常复杂的布局时,可以考虑使用自定义的view来取代,这样就可以减少onmeasure测量和onlayout摆放的次数,从而提高app的性能。
②尽量复用listview的adapter中的getview方法,不要重复获取实例。
列表在滑动的时候,不要去进行元素的更新。这就是说,设置listview的滑动监听,只有在滑动停止的时候,才会进行数据或图片的加载。而在滑动的时候,只显示图片的默认值,或者缩略图
③背景和图片等内存分配优化:尽量减少整个背景当中不必要的背景设置。图片要进行压缩处理,还有对gc的处理,当在16ms对进行渲染的时候,频繁触发gc从而导致的内存泄漏的过程。
④避免ANR:不要在ui线程中做耗时的操作,一定要开启子线程去做耗时的操作,常用Android中已经提供好的良好的异步处理框架。