Android系统APP性能优化在开发应用程序时应该注意的点有哪些

电子说

1.2w人已加入

描述

说到 Android 系统手机,大部分人的印象是用了一段时间就变得有点卡顿,有些程序在运行期间莫名其妙的出现崩溃,打开系统文件夹一看,发现多了很多文件,然后用手机管家 APP 不断地进行清理优化 ,才感觉运行速度稍微提高了点,就算手机在各种性能跑分软件面前分数遥遥领先,还是感觉无论有多大的内存空间都远远不够用。相信每个使用 Android 系统的用户都有过以上类似经历,确实,Android 系统在流畅性方面不如 IOS 系统,为何呢,明明在看手机硬件配置上时,Android 设备都不会输于 IOS 设备,甚至都强于它,关键是在于软件上。

造成这种现象的原因是多方面的,简单罗列几点如下:

其实近年来,随着 Android 版本不断迭代,Google 提供的Android 系统已经越来越流畅,目前最新发布的版本是 Android 8.0 Oreo 。但是在国内大部分用户用的 Android 手机系是各大厂商定制过的版本,往往不是最新的原生系统内核,可能绝大多数还停留在 Android 5.0 系统上,甚至 Android 6.0 以上所占比例还偏小,更新存在延迟性。

由于 Android 系统源码是开放的,每个人只要遵从相应的协议,就可以对源码进行修改,那么国内各个厂商就把基于 Android 源码改造成自己对外发布的系统,比如我们熟悉的小米手机 Miui 系统、华为手机 EMUI 系统、Oppo 手机 ColorOS 系统等。由于每个厂商都修改过 Android 原生系统源码,这里面就会引发一个问题,那就是著名的Android 碎片化问题,本质就是不同 Android 系统的应用兼容性不同,达不到一致性。

由于存在着各种 Android 碎片化和兼容性问题,导致 Android 开发者在开发应用时需要对不同系统进行适配,同时每个 Android 开发者的开发水平参差不齐,写出来的应用性能也都存在不同类型的问题,导致用户在使用过程中用户体验感受不同,那么有些问题用户就会转化为 Android 系统问题,进而影响对Android 手机的评价。

性能优化

今天想说的重点是Android APP 性能优化,也就是在开发应用程序时应该注意的点有哪些,如何更好地提高用户体验。一个好的应用,除了要有吸引人的功能和交互之外,在性能上也应该有高的要求,即时应用非常具有特色,在产品前期可能吸引了部分用户,但是用户体验不好的话,也会给产品带来不好的口碑。

那么一个好的应用应该如何定义呢?主要有以下三方面:

业务/功能

符合逻辑的交互

优秀的性能

众所周知,Android 系统作为以移动设备为主的操作系统,硬件配置是有一定的限制的,虽然配置现在越来越高级,但仍然无法与 PC 相比,在 CPU 和内存上使用不合理或者耗费资源多时,就会碰到内存不足导致的稳定性问题、CPU 消耗太多导致的卡顿问题等。

面对问题时,大家想到的都是联系用户,然后查看日志,但殊不知有关性能类问题的反馈,原因也非常难找,日志大多用处不大,为何呢?因为性能问题大部分是非必现的问题,问题定位很难复现,而又没有关键的日志,当然就无法找到原因了。这些问题非常影响用户体验和功能使用,所以了解一些性能优化的一些解决方案就显得很重要了,并在实际的项目中优化我们的应用,进而提高用户体验。

四个方面

可以把用户体验的性能问题主要总结为4个类别:

流畅

稳定

省电、省流量

安装包小

性能问题的主要原因是什么,原因有相同的,也有不同的,但归根到底,不外乎内存使用、代码效率、合适的策略逻辑、代码质量、安装包体积这一类问题,整理归类如下:

APP

从图中可以看到,打造一个高质量的应用应该以4个方向为目标:快、稳、省、小。

快:使用时避免出现卡顿,响应速度快,减少用户等待的时间,满足用户期望。

稳:减低 crash 率和 ANR 率,不要在用户使用过程中崩溃和无响应。

省:节省流量和耗电,减少用户使用成本,避免使用时导致手机发烫。

小:安装包小可以降低用户的安装成本。

要想达到这4个目标,具体实现是在右边框里的问题:卡顿、内存使用不合理、代码质量差、代码逻辑乱、安装包过大,这些问题也是在开发过程中碰到最多的问题,在实现业务需求同时,也需要考虑到这点,多花时间去思考,如何避免功能完成后再来做优化,不然的话等功能实现后带来的维护成本会增加。

一、卡顿优化

Android 应用启动慢,使用时经常卡顿,是非常影响用户体验的,应该尽量避免出现。卡顿的场景有很多,按场景可以分为4类:UI 绘制、应用启动、页面跳转、事件响应,如图:

APP

这4种卡顿场景的根本原因可以分为两大类:

•   界面绘制。主要原因是绘制的层级深、页面复杂、刷新不合理,由于这些原因导致卡顿的场景更多出现在 UI 和启动后的初始界面以及跳转到页面的绘制上。

•   数据处理。导致这种卡顿场景的原因是数据处理量太大,一般分为三种情况:

数据在处理 UI 线程,

数据处理占用 CPU 高,导致主线程拿不到时间片,

内存增加导致 GC 频繁,从而引起卡顿。

引起卡顿的原因很多,但不管怎么样的原因和场景,最终都是通过设备屏幕上显示来达到用户,归根到底就是显示有问题,所以,要解决卡顿,就要先了解 Android 系统的显示原理。

Android系统显示原理

Android 显示过程可以简单概括为:Android 应用程序把经过测量、布局、绘制后的 surface 缓存数据,通过 SurfaceFlinger 把数据渲染到显示屏幕上, 通过 Android 的刷新机制来刷新数据。也就是说应用层负责绘制,系统层负责渲染,通过进程间通信把应用层需要绘制的数据传递到系统层服务,系统层服务通过刷新机制把数据更新到屏幕上。

我们都知道在 Android 的每个 View 绘制中有三个核心步骤:Measure、Layout、Draw。具体实现是从 ViewRootImp 类的performTraversals() 方法开始执行,Measure 和 Layout都是通过递归来获取 View 的大小和位置,并且以深度作为优先级,可以看出层级越深、元素越多、耗时也就越长。

真正把需要显示的数据渲染到屏幕上,是通过系统级进程中的 SurfaceFlinger 服务来实现的,那么这个SurfaceFlinger 服务主要做了哪些工作呢?如下:

响应客户端事件,创建 Layer 与客户端的 Surface 建立连接。

接收客户端数据及属性,修改 Layer 属性,如尺寸、颜色、透明度等。

将创建的 Layer 内容刷新到屏幕上。

维持 Layer 的序列,并对 Layer 最终输出做出裁剪计算。

既然是两个不同的进程,那么肯定是需要一个跨进程的通信机制来实现数据传递,在 Android 显示系统中,使用了 Android 的匿名共享内存:SharedClient,每一个应用和 SurfaceFlinger 之间都会创建一个SharedClient ,然后在每个 SharedClient 中,最多可以创建 31 个 SharedBufferStack,每个 Surface 都对应一个 SharedBufferStack,也就是一个 Window。

一个 SharedClient 对应一个Android 应用程序,而一个 Android 应用程序可能包含多个窗口,即 Surface 。也就是说 SharedClient 包含的是 SharedBufferStack的集合,其中在显示刷新机制中用到了双缓冲和三重缓冲技术。最后总结起来显示整体流程分为三个模块:应用层绘制到缓存区,SurfaceFlinger 把缓存区数据渲染到屏幕,由于是不同的进程,所以使用 Android 的匿名共享内存 SharedClient 缓存需要显示的数据来达到目的。

除此之外,我们还需要一个名词:FPS。FPS 表示每秒传递的帧数。在理想情况下,60 FPS 就感觉不到卡,这意味着每个绘制时长应该在16 ms 以内。但是 Android 系统很有可能无法及时完成那些复杂的页面渲染操作。Android 系统每隔 16ms 发出 VSYNC 信号,触发对 UI 进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需的 60FPS。如果某个操作花费的时间是 24ms ,系统在得到 VSYNC 信号时就无法正常进行正常渲染,这样就发生了丢帧现象。那么用户在 32ms 内看到的会是同一帧画面,这种现象在执行动画或滑动列表比较常见,还有可能是你的 Layout 太过复杂,层叠太多的绘制单元,无法在 16ms 完成渲染,最终引起刷新不及时。

卡顿根本原因

根据Android 系统显示原理可以看到,影响绘制的根本原因有以下两个方面:

绘制任务太重,绘制一帧内容耗时太长。

主线程太忙,根据系统传递过来的 VSYNC 信号来时还没准备好数据导致丢帧。

绘制耗时太长,有一些工具可以帮助我们定位问题。主线程太忙则需要注意了,主线程关键职责是处理用户交互,在屏幕上绘制像素,并进行加载显示相关的数据,所以特别需要避免任何主线程的事情,这样应用程序才能保持对用户操作的即时响应。

总结起来,主线程主要做以下几个方面工作:

UI 生命周期控制

系统事件处理

消息处理

界面布局

界面绘制

界面刷新

除此之外,应该尽量避免将其他处理放在主线程中,特别复杂的数据计算和网络请求等。

性能分析工具

性能问题并不容易复现,也不好定位,但是真的碰到问题还是需要去解决的,那么分析问题和确认问题是否解决,就需要借助相应的的调试工具,比如查看 Layout 层次的 Hierarchy View、Android 系统上带的 GPU Profile 工具和静态代码检查工具 Lint 等,这些工具对性能优化起到非常重要的作用,所以要熟悉,知道在什么场景用什么工具来分析。

1. Profile GPU Rendering

在手机开发者模式下,有一个卡顿检测工具叫做:Profile GPU Rendering,如图:

APP

它的功能特点如下:

一个图形监测工具,能实时反应当前绘制的耗时

横轴表示时间,纵轴表示每一帧的耗时

随着时间推移,从左到右的刷新呈现

提供一个标准的耗时,如果高于标准耗时,就表示当前这一帧丢失

2. TraceView

TraceView 是 Android SDK 自带的工具,用来分析函数调用过程,可以对 Android 的应用程序以及 Framework 层的代码进行性能分析。它是一个图形化的工具,最终会产生一个图表,用于对性能分析进行说明,可以分析到每一个方法的执行时间,其中可以统计出该方法调用次数和递归次数,实际时长等参数维度,使用非常直观,分析性能非常方便。

3. Systrace UI 性能分析

Systrace 是 Android 4.1及以上版本提供的性能数据采样和分析工具,它是通过系统的角度来返回一些信息。它可以帮助开发者收集 Android 关键子系统,如 surfaceflinger、WindowManagerService 等 Framework 部分关键模块、服务、View系统等运行信息,从而帮助开发者更直观地分析系统瓶颈,改进性能。Systrace 的功能包括跟踪系统的 I/O 操作、内核工作队列、CPU 负载等,在 UI 显示性能分析上提供很好的数据,特别是在动画播放不流畅、渲染卡等问题上。

优化建议

1. 布局优化

布局是否合理主要影响的是页面测量时间的多少,我们知道一个页面的显示测量和绘制过程都是通过递归来完成的,多叉树遍历的时间与树的高度h有关,其时间复杂度 O(h),如果层级太深,每增加一层则会增加更多的页面显示时间,所以布局的合理性就显得很重要。

那布局优化有哪些方法呢,主要通过减少层级、减少测量和绘制时间、提高复用性三个方面入手。

总结如下:

减少层级。合理使用 RelativeLayout 和 LinerLayout,合理使用Merge。

提高显示速度。使用 ViewStub,它是一个看不见的、不占布局位置、占用资源非常小的视图对象。

布局复用。可以通过 标签来提高复用。

尽可能少用wrap_content。wrap_content 会增加布局 measure 时计算成本,在已知宽高为固定值时,不用wrap_content 。

删除控件中无用的属性。

2. 避免过度绘制

过度绘制是指在屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次重叠的 UI 结构中,如果不可见的 UI 也在做绘制的操作,就会导致某些像素区域被绘制了多次,从而浪费了多余的 CPU 以及 GPU 资源。

如何避免过度绘制呢,如下:

布局上的优化。移除 XML 中非必须的背景,移除 Window 默认的背景、按需显示占位背景图片

自定义View优化。使用 canvas.clipRect()来帮助系统识别那些可见的区域,只有在这个区域内才会被绘制。

3. 启动优化

通过对启动速度的监控,发现影响启动速度的问题所在,优化启动逻辑,提高应用的启动速度。启动主要完成三件事:UI 布局、绘制和数据准备。

因此启动速度优化就是需要优化这三个过程:

UI 布局。应用一般都有闪屏页,优化闪屏页的 UI 布局,可以通过 Profile GPU Rendering 检测丢帧情况。

启动加载逻辑优化。可以采用分布加载、异步加载、延期加载策略来提高应用启动速度。

数据准备。数据初始化分析,加载数据可以考虑用线程初始化等策略。

4. 合理的刷新机制

在应用开发过程中,因为数据的变化,需要刷新页面来展示新的数据,但频繁刷新会增加资源开销,并且可能导致卡顿发生,因此,需要一个合理的刷新机制来提高整体的 UI 流畅度。

合理的刷新需要注意以下几点:

尽量减少刷新次数。

尽量避免后台有高的 CPU 线程运行。

缩小刷新区域。

5. 其他

在实现动画效果时,需要根据不同场景选择合适的动画框架来实现。有些情况下,可以用硬件加速方式来提供流畅度。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分