一、 对象的使用
手机设备分配给每个应用的内存是有限的,合理地使用对象利于移动设备内存压力的减少,这样就减少了GC的操作次数,从而提升用户交互体验。合理地创建和使用对象,主要有以下几点:
1) Activity对象的复用。Activity对象是很占用资源的,所以应该尽可能地复用activity栈中的实例,从而应用内存的消耗。可以设置Activity 的launchMode或者使用Intent的Flag来控制新activity的启动方式, 以复用栈中的对象。
2) ListView优化。这个组件是很常用和高频的,有效地优化能直接提升前端的交互体验。
2.1 convertView。Listview有个叫Recycler的反复循环器,从而实现对item view 的复用,减少xml文件的IO操作过程,直接提升前端交互性能。
2.2 ViewHolder。这是很常用的listview优化方法,能大大较少findViewById查找view子树的实例次数。
2.3 异步加载。如可以使用Asynctask异步任务来加载子项的图片内容,避免图片加载时影响主线程的界面交互体验。
2.4 分页加载。包括接口数据分页加载和前端可见项加载两种。可见项加载即实现onScrollListener的监听,让前端仅加载页面可见的项的图片数据,从而直接降低了listview加载大量图片数据的压力。
3) 静态变量与方法。相比于非静态常量,静态常量会少一个在初始化器中的初始化过程,调用效率会更高。直接使用静态方法,相比于要创建对象的实现效率会提升20%左右。
4) 库函数的使用。尽量使用API提供的库函数,避免重复造轮子,因为所封装的库函数很多都是用c/c++实现的,执行效率会更高。
5) 数据类型的使用。
5.1 受定点处理器的影响,float的速度回比int慢一半左右的速度,所以应该优先使用int类型。
5.2 Android官方强烈建议在项目中少用enum。其的代价是比没用enum的变量多好几倍的内存空间,这是由于初始化enum对象需要更多的方法数。
6) Getter/Setter。Android中虚方法的调用比直接访问变量需要更多的耗费,并且直接访问的话速度是调用虚方法的的数倍。
7) 少创建对象。对象的创建意味着需要一定的内存,内存占用越多,也就越容易促发gc的活动,也就直接导致了界面的卡顿(gc执行操作时,界面会停止其他操作的响应)。所以程序中应该合理复用内存中的对象,减少对象的创建。
二、 布局优化
1) 工具Lint和Hierarychy Viewer。Lint是个非常强大的规范提示工具,能对你的布局优化提示相应的改良建议。Hierarychy Viewer非常好用,能测出你的界面在该手机里的渲染时间,能找出你界面布局上面的性能瓶颈,这样你就能针对性地去调整布局,通常来说,界面越趋于扁平化,布局渲染得就越快,性能就越好。
2)include。提高界面布局的复用。在我们的布局中使用该标签能提高对公共布局的复用,从而提高界面布局设计的灵活性。
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<include
layout="@layout/common_header"
android:id="@+id/act_flt_search"/>
......
</LinearLayout>
3) merge。减少嵌套。使用该标签能直接减少ViewGroup的使用,这样也就减少了该对象的实例,从而让整个布局更加轻。
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/show_color"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/show_shine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</merge>
4) ViewStub。惰性加载。该功能实现view对象在被需要的时候才加载出来,在未被加载出来前是不占用资源的,相比于GONE属性,其没被加载是不会创建实例的,也就不会占用资源,更加轻量。
<ViewStub
android:id="@+id/inflated_about"
android:inflatedId="@+id/layout_inflated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout="@layout/act_about"/>
5)扁平化设计。界面布局越轻巧、扁平化,界面交互就越流畅,对于一些多层嵌套的布局,可以考虑将其独立成一个类来绘制,直接减少层级的嵌套,提高性能。
6)自定义view。onMeasure()、onLayout()、onDraw()中应减少过重的任务(耗时、好资源),并减少三个方法的执行次数。
三、 内存优化
1)图片缓存。Bitmap一直是个内存大户,常常引起内存方面的异常,所以需要对其设计合理的缓存策略,控制其在应用中占用内存大小的范围。可以使用LruCache,设置一定的缓存大小,加载图片的采样率,对图片在内存的大小做一定的控制。当图片不再显示的,需要对bitmap对象进行回收,从而减少内存的压,减少gc促发的频率。
2)图片适配。Android为了解决应用程序适配不同分辨率图片显示的问题,需要准备好对应分辨率的图片,否则的话就可能造成一些机型图片显示的失真或者是内存占用异常的问题。如果要想应用只管理一套图片,可以考虑将图片放在特殊文件中,如asset或者nodpi(大小不被拉伸)中,这样就不需要设计师准备多套图,但在视觉效果上一些图片会有所失真。
3)慎用依赖注入。依赖注入框架的优点就是非常方面我们的开发, 代码结构更简洁和规范,但其的代价就是在实例化ui组件时,需要创建额外的对象,以及额外的时间开销,这样时间和空间的成本是挺大的。而使用原来直接的实例化形式,反而效率会更高,性能也越好。
4)多进程的使用。多进程能扩大应用的使用范围,但相比来说这样需要更多的内存,所以应用中少用多进程,减少多进程的使用能直接减轻内存的压力。
5)service的使用。组件service常用于台任务的执行,但这样也额外占用了内存,所以使用完后需要去结束它,可以使用intentservice来达到该目的。
6)数据库操作。SQLiteDatabase和cursor对象非常占用资源,为了避免内存资源的消耗,使用完需要及时关闭,以免造成过多的资源的浪费,减轻内存的压力。
今年年初Google发布了关于新年优化的16个视频,详情
https://www.youtube.com/playlist?list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE
内存优化涉及方方面面,需要优化的细节很多,所以需要养成在开发过程中不断优化的习惯,这样做出来的app才会体验更爽。