高质量的应用目标:快、稳、省、小
-
快:使用时避免出现卡顿,响应速度快,减少用户等待的时间,满足用户期望。
-
稳:减低 crash 率和 ANR 率,不要在用户使用过程中崩溃和无响应。
-
省:节省流量和耗电,减少用户使用成本,避免使用时导致手机发烫。
-
小:安装包小可以降低用户的安装成本
![](https://img.haomeiwen.com/i2179862/debf846771639fc9.png)
1. 卡顿优化
![](https://img.haomeiwen.com/i2179862/078642466c5fe388.png)
归根结底:现实问题
Android系统显示原理
应用层绘制好后,通过跨进程通信把需要的数据传递到系统层,通过系统层的SurfaceFlinger把数据渲染到显示屏幕上,通过Android的刷新机制来刷新数据。
应用层
- View绘制核心步骤:Measure、Layout、Draw
- 从 ViewRootImp类的performTraversals() 方法开始执行,Measure和Layout都是通过递归来获取View的大小和位置,并且以深度作为优先级,层级越深、元素越多、耗时也就越长。
绘制方式
- 软件绘制(CPU)
- 硬件加速(GPU,3.0后全面支持)
应用层GPU绘制缺点:
-
耗电
GPU功耗比CPU高 -
兼容问题
某些接口和函数不支持硬件加速 -
内存大
使用OpenGL接口至少需要8MB内存
绘制过程
- CPU准备数据,通过Driver层把数据交给CPU渲染
- CPU主要负责Measure、Layout、Record、Execute的数据计算工作
GPU负责栅格化(Rasterization)、渲染 - 图形API不允许CPU直接与GPU进行通信,通过图形驱动层(Graphics Driver)来连接这两部分
- 图形驱动层维护了一个队列,CPU把display list 添加到队列中,GPU从中取出数据进行绘制,最终显示。
系统层
通过系统级进程中的SurfaceFlinger服务来真正把需要显示的数据渲染到屏幕上
-
响应客户端事件,创建Layer与客户端的Surface建立连接。
-
接收客户端数据及属性,修改Layer属性,如尺寸、颜色、透明度等。
-
将创建的Layer内容刷新到屏幕上。
-
维持Layer的序列,并对Layer最终输出做出裁剪计算。
![](https://img.haomeiwen.com/i2179862/280de9c4fd267e8f.png)
-
应用层绘制到缓存区,SurfaceFlinger把缓存区数据渲染到屏幕,由于是不同的进程,所以使用Android的匿名共享内存SharedClient缓存需要显示的数据来达到目的。
-
FPS表示每秒传递的帧数。Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染。主要场景执行动画或滑动ListView。
-
CPU或GPU负载过重,布局过于复杂,16ms不能进行渲染,UI层叠太多,动画执行次数过多,刷新不及时等容易丢帧。
刷新机制
双缓冲
Framebuffer带来残影问题
VSYNC(Vertical Synchronization)
CPU主动查询保证数据是否准备好,效率低
定时中断,收到信号,CPU处理各帧数据
Choreographer:起调度作用
收到VSYNC信号,调用用户设置的回调函数
- CALLBACK_INPUT : 输入事件有关
- CALLBACK_ANIMATION : 与动画有关
- CALLBACK_TRAVERSAL : UI控件绘制有关
卡顿根本原因
- 绘制任务太重,绘制一帧内容耗时太长
- 主线程太忙,根据系统传递过来的VSYNC信号来时还没准备好数据导致丢帧。
主线程主要做以下几个方面工作
- UI生命周期控制
- 系统事件处理
- 消息处理
- 界面布局
- 界面绘制
- 界面刷新
- 除此之外,应该尽量避免将其他处理放在主线程中,特别复杂的数据计算和网络请求等。
性能分析工具
1. Profile GPU Rendering
-
一个图形监测工具,能实时反应当前绘制的耗时
-
横轴表示时间,纵轴表示每一帧的耗时
-
随着时间推移,从左到右的刷新呈现
-
提供一个标准的耗时,如果高于标准耗时,就表示当前这一帧丢失
-
可以通过adb shell dumpsys gfxinfo com..把具体耗时输出到日志来进行查看分析
![](https://img.haomeiwen.com/i2179862/1ea46c68527b4163.png)
2. TraceView
-
TraceView是Android SDK自带的工具,用来分析函数调用过程,可以对Android的应用程序以及Framework层的代码进行性能分析。
-
它是一个图形化的工具,最终会产生一个图表,用于对性能分析进行说明,可以分析到每一个方法的执行时间,其中可以统计出该方法调用次数和递归次数,实际时长等参数维度,使用非常直观,分析性能非常方便。
![](https://img.haomeiwen.com/i2179862/68a31b5777b67b91.png)
3. Systrace
![](https://img.haomeiwen.com/i2179862/cdab3c14f3f7830d.png)
-
Systrace是Android 4.1及以上版本提供的性能数据采样和分析工具,它是通过系统的角度来返回一些信息。
-
它可以帮助开发者收集Android关键子系统,如Surfaceflinger、WindowManagerService等Framework部分关键模块、服务、View系统等运行信息,从而帮助开发者更直观地分析系统瓶颈,改进性能。
-
Systrace的功能包括跟踪系统的I/O操作、内核工作队列、CPU负载等,在UI显示性能分析上提供很好的数据,特别是在动画播放不流畅、渲染卡等问题上。
卡顿优化建议
1 布局优化(HieracrchyViewer&Lint)
- 减少层级。
- 合理使用RelativeLayout和LinerLayout,合理使用Merge。
- 提高显示速度。
- 使用ViewStub。
- 布局复用。
- 通过标签来提高复用。
- 尽可能少用wrap_content。
- 增加布局measure时计算成本,在已知宽高为固定值时,不用wrap_content
- 删除控件中无用的属性。
2 避免过度绘制
过度绘制
在屏幕上的某个像素在同一帧的时间内被绘制了多次,从而浪费了多余的CPU以及GPU。
布局上的优化:
- 移除XML中非必须的背景
- 移除Window默认的背景
- 按需显示占位背景图片
自定义View优化:
使用 canvas.clipRect()来帮助系统识别那些可见的区域,只有在这个区域内才会被绘制。
开发者选项:调试GPU过度重绘
- 无色:1
- 蓝色:1+
- 绿色:2+
- 淡红:3+(不超过1/4屏可接受)
- 深红:4++(严重影响性能,需优化)
![](https://img.haomeiwen.com/i2179862/05424c507e1ff7b0.png)
3 启动优化
-
通过对启动速度的监控,发现影响启动速度的问题所在,优化启动逻辑,提高应用的启动速度。
-
启动主要完成三件事:
UI布局
绘制
数据准备 -
UI布局。
应用一般都有闪屏页,优化闪屏页的UI布局,可以通过Profile GPU Rendering检测丢帧情况。使用HierarchyView检查布局优化 -
启动加载逻辑优化。
可以采用分布加载、异步加载、延期加载策略来提高应用启动速度。
![](https://img.haomeiwen.com/i2179862/4fc1d9f8e741d1c9.png)
- 数据准备。
数据初始化分析,加载数据可以考虑用线程初始化等策略。
4 合理的刷新机制
-
频繁刷新会增加资源开销,并且可能导致卡顿发生,因此:
-
尽量减少刷新次数。
-
尽量避免后台有高的CPU线程运行。
-
缩小刷新区域。
5 提升动画性能
-
帧动画:一组图片按顺序显示。图片过多,消耗资源,效果最差。
-
补间动画:只需要定义开始和结束关键帧,之间效果自动生成。
局限性:
只能用于View对象,只有继承于View或View的控件;
只有四种动画:淡入淡出、缩放、平移、旋转。
只改变了显示效果,没有改变真正属性。 -
属性动画:可以应用于View,和任何对象。持续时间、时间插值、重复次数行为、动画系和、帧刷新延迟。相对于不见动画,重绘少,性能更佳。
-
在实现动画效果时,需要根据不同场景选择合适的动画框架来实现。有些情况下,可以用硬件加速方式来提供流畅度。
2. 内存优化
Android内存管理机制
-
Android应用都是在 Android的虚拟机上运行,应用程序的内存分配与垃圾回收都是由虚拟机完成的。
-
在Android系统,虚拟机有两种运行模式:Dalvik和ART。
工具1 Memory Monitor
Memory Monitor是一款使用非常简单的图形化工具,可以很好地监控系统或应用的内存使用情况,主要有以下功能:
- 显示可用和已用内存,并且以时间为维度实时反应内存分配和回收情况。
- 快速判断应用程序的运行缓慢是否由于过度的内存回收导致。
- 快速判断应用是否由于内存不足导致程序崩溃。
工具2 Heap Viewer
Heap Viewer的主要功能是查看不同数据类型在内存中的使用情况,可以看到当前进程中的Heap Size的情况,分别有哪些类型的数据,以及各种类型数据占比情况。通过分析这些数据来找到大的内存对象,再进一步分析这些大对象,进而通过优化减少内存开销,也可以通过数据的变化发现内存泄漏。
工具3 Allocation Tracker
Memory Monitor和Heap Viewer都可以很直观且实时地监控内存使用情况,还能发现内存问题,但发现内存问题后不能再进一步找到原因,或者发现一块异常内存,但不能区别是否正常,同时在发现问题后,也不能定位到具体的类和方法。这时就需要使用另一个内存分析工具Allocation Tracker,进行更详细的分析,Allocation Tracker可以分配跟踪记录应用程序的内存分配,并列出了它们的调用堆栈,可以查看所有对象内存分配的周期。
工具4 Memory Analyzer Tool(MAT)
MAT是一个快速,功能丰富的Java Heap分析工具,通过分析Java进程的内存快照HPROF分析,从众多的对象中分析,快速计算出在内存中对象占用的大小,查看哪些对象不能被垃圾收集器回收,并可以通过视图直观地查看可能造成这种结果的对象。
常见内存泄漏场景
-
资源性对象未关闭。比如Cursor、File文件等,往往都用了一些缓冲,在不使用时,应该及时关闭它们。
-
注册对象未注销。比如事件注册后未注销,会导致观察者列表中维持着对象的引用。
-
类的静态变量持有大数据对象。
-
非静态内部类的静态实例。
-
Handler临时性内存泄漏。如果Handler是非静态的,容易导致Activity或Service不会被回收。
-
容器中的对象没清理造成的内存泄漏。
-
WebView。WebView存在着内存泄漏的问题,在应用中只要使用一次WebView,内存就不会被释放掉。
LeakCanary 第三方开源库
这是一个检测内存泄漏的开源库,可以在发生内存泄漏时告警,并且生成leak tarce分析泄漏位置,同时可以提供Dump文件进行分析。
优化内存空间
-
对象引用。
强引用、软引用、弱引用、虚引用四种引用类型,根据业务需求合理使用不同,选择不同的引用类型。 -
减少不必要的内存开销。
注意自动装箱,增加内存复用,比如有效利用系统自带的资源、视图复用、对象池、Bitmap对象的复用。 -
使用最优的数据类型。
比如针对数据类容器结构,可以使用ArrayMap数据结构,避免使用枚举类型,使用缓存Lrucache等等。 -
图片内存优化。
可以设置位图规格,根据采样因子做压缩,用一些图片缓存方式对图片进行管理等等。
3. 稳定性优化
-
提高代码质量。比如开发期间的代码审核,看些代码设计逻辑,业务合理性等。
-
代码静态扫描工具。常见工具有Android Lint、Findbugs、Checkstyle、PMD等等。
-
Crash监控。把一些崩溃的信息,异常信息及时地记录下来,以便后续分析解决。
-
Crash上传机制。在Crash后,尽量先保存日志到本地,然后等下一次网络正常时再上传日志信息。
4. 耗电优化
-
5.0之后专门引入了一个获取设备上电量消耗信息的API:Battery Historian。一款由Google提供的Android系统电量图形化数据分析工具,直观地展示出手机的电量消耗过程,通过输入电量分析文件,显示消耗情况,最后提供一些可供参考电量优化的方法。
-
计算优化,避开浮点运算等。
-
避免WakeLock使用不当。
休眠意味着CPU频率降低,需要做一些需要大量运算的任务时,所以需要唤醒CPU -
使用JobScheduler
--
5. 安装包大小瘦身
-
代码混淆。
使用ProGuard代码混淆器工具,它包括压缩、优化、混淆等功能。 -
资源优化。
比如使用Android Lint删除冗余资源,资源文件最少化等。 -
图片优化。
比如利用AAPT工具对PNG格式的图片做压缩处理,降低图片色彩位数等。 - 避免重复功能的库,使用WebP图片格式等。
-
插件化。
比如功能模块放在服务器上,按需下载,可以减少安装包大小。
网友评论