基础原理
绘制原理(16ms原则):Android系统每隔16ms发出VSync信号,触发对UI进行渲染,这就意味着Android系统要求每一帧都要在16ms这个时间内绘制渲染完成,从而保证流畅的用户体验。
UI绘制机制:CPU和GPU是我们智能手机的标配,而绝大多数的画面渲染都依赖这两个硬件。CPU负责计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等操作;GPU负责栅格化操作,将UI元素绘制到屏幕上,所谓栅格化即把Button、Shape、Path、Bitmap等组件拆分到不同的像素上进行显示,完成绘制工作,这个操作很耗时,所以引入GPU可以加速栅格化的操作。具体在Android系统中,文字的显示是先经过CPU换算成纹理(Texture),再传给GPU渲染;而图片的显示是经过CPU计算加载到内存中,再传给GPU渲染;动画的显示是结合图片和文字的过程。
绘制优化
过度绘制的意思是屏幕上的某一个像素在同一帧的时间内被绘制了多次。
1、避免过度绘制Overdraw
具体优化方案:
(1)移除系统默认的window背景颜色:由于activity的Theme带有默认的背景颜色,会导致我们新创建的activity默认带有背景颜色,可以把背景颜色去除,减少一次绘制。
(2)移除嵌套布局中相同的背景颜色: 如果同时存在多个布局层叠的情况,可以考虑只对最上层的布局设置背景颜色,不用每个子布局或子控件都设置背景颜色,过多相同的背景颜色层叠会导致过度绘制;
2、降低onDraw()的复杂度
(1)避免在onDraw()中创建新的局部对象:因为onDraw()方法在绘制时可能会被频繁调用,会产生大量的临时对象,占用过多的内存,而且会频繁触发GC消耗系统性能。
(2)避免在onDraw()中执行耗时操作:因为在onDraw()中执行耗时操作会抢占CPU的时间片,导致绘制过程不流畅,应该把耗时操作放到子线程中。
3、常用的检测过度绘制工具
(1)Show GPU Overdraw:直接开启开发者调试中的“调试GPU过度绘制”选项,可以在屏幕上直观看到页面过度绘制的情况。
(2)ProfileGPU Rendering:开启GPU呈现模式之后,当你的应用程序在运行时,你会看到一排柱状图在屏幕上,从左到右动态地显示,每一个垂直的柱状图代表一帧的渲染,越长的垂直柱状图表示这一帧需要渲染的时间越长。随着需要渲染的帧数越来越多,他们会堆积在一起,这样你就可以观察到这段时间帧率的变化。
布局优化
优化的本质:尽量减少页面布局的层级。
1、选择合适的ViewGroup布局
性能耗费低的布局 = 功能简单 = FrameLayout、LinearLayout
性能耗费高的布局 = 功能复杂 = RelativeLayout
嵌套所耗费的性能 > 单个布局本身耗费的性能(1)如果使用LinearLayout、 FrameLayout或者RelativeLayout都能实现相同层级的布局,尽量使用LinearLayout和FrameLayout,因为RelativeLayout加载时会相对比较复杂,增加性能开销;
(2)如果实现相同的布局,使用LinearLayout或FrameLayout需要嵌套实现,而使用RelativeLayout不需要嵌套,尽量使用RelativeLayout,避免加深Layout层级;
(3)尽量不要嵌套使用RelativeLayout;
(4)尽量不要在嵌套的LinearLayout中使用weight属性;
(5)采用自定义View代替复杂嵌套的深层级布局(在业务需求、无法优化情况下);
2、使用布局标签
(1) <merge>标签:使用该标签可以减少布局的层级, <merge>标签可以作为被引用布局的根标签,替换掉不必要的根布局,通常配合<include>标签使用;
(2)<include>标签:使用该标签可以增加布局的复用性,把一些常用的公共布局抽取出来,方便其它布局引用;
(3)<ViewStub>标签:使用该标签来加载一些默认不显示的布局,它是一个轻量级且默认不可见的视图,可以根据需求动态加载一个布局,达到惰性加载的效果,一般用于加载网络请求失败后的页面;
3、常用的检查布局的工具
(1)Hierarchy Viewer:方便查看页面布局的结构;
(2)Layout Inspector:方便查看页面布局的结构,在AS3.0以上版本才有集成;
(3)Lint:Android Studio提供的代码扫描分析工具;
内存优化
1、Android的内存管理机制
Android系统内存分配与回收方式
(1)内存分配:Android中的内存分配是弹性的,系统会根据当前设备的性能参数、RAM空间大小自动为每个进程分配一定的内存空间,并随着进程的不断运行自动分配额外的内存空间,但不会超过最大内存限制空间。也就是说Android中的内存分配策略是尽可能让更多的进程存活在内存中,保证重新打开进程时能快速响应,提高用户的体验。
(2)内存回收:当内存紧张时,Android系统会根据进程优先级的高低杀死一些进程,释放内存空间;在正常情况下,后台进程和空进程都可能会被系统杀死,后台进程采用LRUCache的缓存算法,当队列满时会杀死最早创建和最少使用的进程,而空进程随时会被系统杀死;当内存不足的情况下,系统才会考虑回收前台进程、可见进程、服务进程三种进程,并按优先级高低进程回收。同时系统为了保证尽可能让更多的进程存活在内存中会优先杀死那些占用内存比较多的进程,因此消耗内存越少的进程越容易存活下来。
2、内存优化方案
1、珍惜Service资源:当Service完成任务后,一定要记得停止它,尽量使用IntentService代替Service,IntentService的好处是当任务完成后会自动停止Service,同时内部维护了一个Thread线程可以执行耗时操作。
2、当UI不可见时释放资源:当前界面UI不可见时,应该释放掉该UI上所占用的资源,可以增加系统缓存进程的能力。可以在Activity中重写onTrimMemory()回调方法,并且当回调级别level为TRIM_MEMORY_UI_HIDDEN时,表明当前Activity的UI已经隐藏,可以进行UI资源的释放了。
3、避免Bitmap滥用:可以根据当前屏幕分辨率对Bitmap中的数据进行压缩,同时采用软引用+LRUCache缓存算法来管理Bitmap,软引用在内存不足时会被GC回收,释放内存。
4、使用优化的数据容器:尽量使用SparseArray代替HashMap。HashMap的实现方式更加消耗内存,因为它需要一个额外的实例对象来记录Mapping操作,而SparseArray更加高效在于它避免了对key的自动装箱,从而避免装箱后的解箱。同时尽量少使用枚举类型、尽量使用StringBuilder进行字符串的拼接,因为String对象的字符串拼接会导致大量中间字符串的产生,GC需要额外回收这些中间字符串。
5、避免使用依赖注入框架
6、使用多进程
性能检测工具
网易开源的Emmagee:主要用于监控单个App的CPU,内存,流量,启动耗时,电量,电流等性能状态的变化,且用户可自定义配置监控的频率以及性能的实时显示,并最终生成一份性能统计文件。
ListView的优化
1、convertView复用
2、ViewHolder内部类
3、分页加载
4、监听AbsListView接口,滑动时停止网络加载图片,滑动停止时才进行网络加载
5、LRUCache图片缓存,第一次网络加载后把图片缓存到内存中,避免重复请求网络拉取数据
6、Bitmap图片压缩,对原始图片进行缩放,防止因图片过大占用太多系统内存
7、使用线程池加载图片,如果采用普通线程的话,当ListView滑动时可能会产生大量的线程
8、开启硬件加速UI渲染,解决UI卡顿的问题
参考
《Android开发进阶:从小工到专家》 第6章 性能优化
《App架构师》第9章 App性能优化系列