本篇博客,主要向你说明,以下两个问题:
- 为什么要内存优化?
- 如何进行内存优化(方法有哪些)?
一、为什么要内存优化?
因为Android平台和Java语言本身的某些特性的缘故,在开发过程中,如果不注意这些特性。可能会导致内存消耗,比其它平台(IOS)和开发语言(C/C++)多得多。所以,我们需要最大化的去避免,额外的内存开销和泄露。所以,通过了解这些特性,并合理的组织你的代码,来减少APP的内存开销和避免内存泄露,写出高质量的代码,提高APP的运行性能。
二、如何进行内存优化(方法有哪些)?
-
关闭不必要的Service。
Service会消耗较多的内存和CPU开销,如果不是很必要,并不建议创建服务。如果只是要在后台跑一个任务,考虑用线程处理或JobScheduler。如果必需要创建一个Service,建议用IntentService来创建。这样,当启动Service的Intent被销毁时,Service也会被销毁。 -
使用Android定制的容器。
当某一容器,只需要存储少量的数据时,建议用Android定制的容器,代替Java提供的容器。比如ArrayMap代替HashMap。因为,通常情况下,APP的Map或Array并不会存储,大量的数据。多数情况下,大部分情况下,都是量少于1000的。所以,在存储量不超过1000的地方,建议用安卓定制的容器去替换。 其它的容器,还有SparseArray, SparseBooleanArray等等。(超过1000的,有必要思考一下,是不是真的需要这么多…) -
避免使用抽象类,除非确实有显著的作用。
因为,在代码架构时,许多有些经验和学过Java设计模式和EffectiveJava的人都知道。好的抽象类设计,可以让你的代码更灵活,维护起来也容易。但是,抽象类,会产生额外的代码。执行这些额外的代码,需要消耗额外的大量内存和CPU资源。所以,在使用抽象类之前,得衡量一下,是否值得花费这些开销。 -
避免内存抖动。
所谓的内存抖动,即是在短时间内,大量的创建、销毁对象。因为这样会导致系统,不断的进行GC操作。少量的GC操作,并不影响性能。但大量的GC操作,就另当别论。比较常见的例子就是,不要在onDraw()方法里面创建对象,因为onDraw()方法会被频繁的调用(这个,AS也自带提示了…) -
使用编译时注解,代替运行时注解。比如,Dagger2。
我个人是不倾向于使用注解的。当然,注解可以在很大程度上,简化开发的复杂度,提高开发的速度。但多数情况下,除非真的特别新(没有可复用的资源)、急、赶的项目,否则,我是不建议使用的,注解,或多或少的增加运行时的开销或额外的apk的体积的消耗。但如果两者必要一个,Google的建议是,牺牲空间换时间。这里的空间是指apk体积占用,而不是内存占用。这一条,仁者见仁了。但从内存优化角度来说,编译时注解[占体积]是优于运行时[占内存和运行时速度]注解的。 -
谨慎使用第三方库。
不少第三方库,处于Demo形态。或者,只顾实现,不管优化。导致内存泄露,额外的内存开销。有的第三方库,里面又嵌套其它第三方库。导致Apk体积的增加和运行时的额外开销(初始化等操作)。在挑选第三方库时,应该谨慎,尽量挑那些star值多的。 -
使用约束布局,减少层次嵌套。
对于复杂的布局,建议使用约束布局代替。因为约束布局,使用的是相对约束,控件与控件,Layout与Layout之间仅存在约束,不存在层次关系。学过findViewById原理的同学可能知道,Android在FindView时,会层层获取,层次越多,查找的就越慢,在绘制UI时,也会更消耗更多的资源和时间。所以,尽量不要嵌套布局。 -
使用ViewStub,延时加载布局。
对于一些一开始不必要展示的布局,使用ViewStub来加载。或者动态添加也行。而不要写死在xml里面,然后设置成GONE。哪怕你设置成GONE,你的布局同样也会占用内存,消耗资源, 可以考虑用动态添加的方式实现。 -
使用动态创建View,取代xml绘制View。
一些简点的界面,可以考虑用动态创建的方式来实现,可以减少Android系统将xml转换成View的这一层开销。这取决于界面的复杂度、实现的复杂度和个人的能力。 -
慎用回调,尤其是将Activity做为参数。
为什么说要慎用回调?对Java新手来说,回调是个摸不着头脑的事情。对Java有点熟的同志来说,回调是个处理异步事件的好帮手。有些新手或半熟手,在对一些异步处理需求的实现上面,为了方便或是没有其它更好的办法处理的时候,就倾向于将Activity做为参数传递过去,等异步处理完后,再直接调用Activity对象的方法,来实现这样的需求。殊不知,这样做,容易导致,Activity的对象在另一个地方被持有,导致页面一直无法被释放,进而导致内存泄露。So,除非万不得已,请别将Activity对象做为参数传递。异处回调处理,可以考虑用EventBus事件通知。万不得已而用之时,则需要关注一下生命周期和对象的释放。