这篇文章主要是介绍了一些小细节的优化技巧,当这些小技巧综合使用起来的时候,对于整个App的性能提升还是有作用的。
虽然选择合适的算法与数据结构可以更好提高App的整体性能,但在这篇文章中不会涉及这方面,有兴趣的可以自己去了解。使用这篇文章中的小技巧作为平时写代码的习惯,这样能够提升代码的效率。
1、内存的合理分配
Android系统里面有一个Generational Heap Memory的模型,系统会根据内存中不同的内存数据类型分别执行不同的GC操作。例如,最近刚分配的对象会放在Young Generation区域,这个区域的对象通常都是会快速被创建并且很快被销毁回收的,同时这个区域的GC操作速度也是比Old Generation区域的GC操作速度更快的。
除了速度差异之外,执行GC操作的时候,所有线程的任何操作都会需要暂停,等待GC操作完成之后,其他操作才能够继续运行。
通常来说,单个的GC并不会占用太多时间,但是大量不停的GC操作则会显著占用帧间隔时间(16ms)。如果在帧间隔时间里面做了过多的GC操作,那么自然其他类似计算,渲染等操作的可用时间就变得少了。
导致GC频繁执行有两个原因:
•Memory Churn内存抖动,内存抖动是因为大量的对象被创建又在短时间内马上被释放。
•瞬间产生大量的对象会严重占用Young Generation的内存区域,当达到阀值,剩余空间不够的时候,也会触发GC。即使每次分配的对象占用了很少的内存,但是他们叠加在一起会增加Heap的压力,从而触发更多其他类型的GC。这个操作有可能会影响到帧率,并使得用户感知到性能问题。
解决上面的问题有简洁直观方法,如果你在Memory Monitor里面查看到短时间发生了多次内存的涨跌,这意味着很有可能发生了内存抖动。
同时我们还可以通过Allocation Tracker来查看在短时间内,同一个栈中不断进出的相同对象。这是内存抖动的典型信号之一。
当你大致定位问题之后,接下去的问题修复也就显得相对直接简单了。例如,你需要避免在for循环里面分配对象占用内存,需要尝试把对象的创建移到循环体之外,自定义View中的onDraw方法也需要引起注意,每次屏幕发生绘制以及动画执行过程中,onDraw方法都会被调用到,避免在onDraw方法里面执行复杂的操作,避免创建对象。对于那些无法避免需要创建对象的情况,我们可以考虑对象池模型,通过对象池来解决频繁创建与销毁的问题,但是这里需要注意结束使用之后,需要手动释放对象池中的对象。
2、不要持有Context的静态引用
内存泄露检查工具
其他对象中的静态成员变量引用Content会造Content不能正常的回收,造成内存泄露。静态变量的内存是不会被回收,直到应用程序退出才回收。比如下图,当Activity finish后造成Activity不能回收。
1 | public class MemoryActivity extends Activity{ |
3、在Activit销毁时应该做的事
1)unregisterReceiver,注销不需要的广播接收。
2)释放和关闭当前界面所有网络资源。(比如我们在使用Volley和Picasso, 在基类onDestroy中释放所有的资源)
1 |
|
3)释放定时任务。
4)释放handler资源。非静态内部类隐式地持有一个外部类对象的引用,如普通执行任务不释放资源可能不出现内存泄露,如果是handler定时执行任务一定会造成内存泄露。
1 | public class HandlerActivity extends Activity{ |
5)释放其引用Activity的资源。(如果其他对象是一个全局的对象,在Activity finish后该对象不回收也会导致Activity不能回收)
4、其他
1)合理的分配任务调度。
2)合理的使用线程池。(本地数据读取,网络数据读取)
3)网络数据优化。 Volley解析
4) 类的内直接使用成员变量访问,避免使用get、set(速度提高3-7倍)。