性能优化实践二

2020-01-29 15:23 阅读 3,073 次 评论 1 条

摘要:

为了对线上APP运行情况有更一步的了解,对性能优化有一个参考的依据,同时展示开发人员优化前后的效果,现在需要收集性能相关的数据,这些数据包括:程序安装包资源分析、界面卡顿检测、内存性能分析、SQLite质量检测、文件IO监控。实现对线上APP全方位的监控,掌握复杂多变的线上环境的运行情况。

一、安装包资源分析

安装包资源分析看起来好像和性能没有多大关系,实际上也属于性能优化更重要的一部分,只不过是线下优化。分析安装包的资源文件,可以列出每个文件的大小,打印出冗余文件的路径,提示无用的资源文件,找出不含alpha通道的PNG图片等等,可以快速帮助开发者优化安装包的大小。一个安装包大小直接影响用户的安装率,安装包越小越好。

影响着一个安装包大小的因素有:代码(第三方库)、图片资源、冗余文件和应用程序的功能,下面分别从这几个方面描述优化的点。

1.1 检查安装包代码(第三方库)

第三方库特意打包的资源,在部分第三方依赖包中往往会出现多个版本,如按功能区分可是有完整版和部分功能版,或者按照架构区分(arm64-v8a、armeabi、armeabi-v7a、mips、mips64、x86、x86_64)可能是调试环境版和生产环境版。

1、armeabi: 第5代、第6代的32位ARM处理器,早期的手机在使用,现在基本很少了。

2、armeabi-v7a: 第7代及以上的 32位ARM 处理器

3、arm64-v8a: 第8代、64位ARM处理器

4、x86: Intel 32位处理器,在平板、模拟器用得比较多。

5、x86_64: Intel 64位处理器,在平板、模拟器用得比较多。


defaultConfig {  
    ndk {  
        abiFilters "armeabi"// 指定ndk需要兼容的ABI(这样其他依赖包里x86,armeabi,arm-v8之类的so会被过滤掉) 
    }  
}

abi是向下兼容的基本原则,即:

  • 只适配armeabi的APP可以跑在armeabi,x86,x86_64,armeabi-v7a,arm64-v8上
  • 只适配armeabi-v7a可以运行在armeabi-v7a和arm64-v8a
  • 只适配arm64-v8a 可以运行在arm64-v8a上

适配方案:

  • 只适配armeabi
  • 优点:基本可以适配所有手机机型,除了淘汰的mips和mips_64
    缺点:在大多数手机上都需要利用辅助ABI或者动态转码来兼容,性能较差

  • 只适配 armeabi-v7a
  • 只是又筛掉了一部分老旧设备,在性能和兼容二者中比较平衡

  • 只适配 arm64-v8
  • 优点:性能最佳
    缺点:只能运行在arm64-v8上,要放弃部分老旧设备用户

有些依赖包会占据项目资源很大一部分。所以按需集成依赖库,可以让项目的对第三方库引用加载和项目打包都能得到优化。

本着一个原则:能够使用原生系统api实现的功能达到一致的效果,不建议引入第三方库。第三库通常封装了较多的功能,体积也有几十KB,大的甚至有几百KB,引入5个200KB的第三方库,无形中增加了1M的大小,所以这里需要罗列出项目里面所有引用到的第三方库,思考几个问题:
引用的库是否必须的?

  • 是否可以对引用的库进行定制?
  • 是否可以用体积更小的第三库替代?
  • 是否可以对第三库的代码执行优化?

基于这几个问题,移动端(IOS和Android)分别统计使用到的库,针对第三方库提供一些优化的思路,比如:

1.1.1 Android项目检测样例

在Android项目中可在gradle文件中,添加混淆,混淆的作用:移除未引用的代码和压缩资源文件。

gradle文件中的minifyEnabled和shrinkResource默认为false,即未开启代码混淆和未开启资源混淆。代码混淆除了可以移除掉未引用的类、方法、变量,还具备优化压缩的功能。同时使用无意义的字母代替类名、方法名和变量,提高代码被反编译的难度。

// 代码混淆
minifyEnabled false
// 移除无用的resource文件
shrinkResources false

1.2 分析冗余或无用文件

冗余文件,指的是重复的文件。先检查项目中哪些是重复的,列出重复文件的路径、名称,现阶段考虑发版前手动删除,后期可以考虑编写工具自动删除,最后只保留其中一份。常见的冗余文件有:图片冗余,多个项目导入同一张图片或者同一张图片被定义了不同的名称;项目里面引入了重复的第三方库等

无用文件,指的是没有被引用的文件。因为开发过程需求的变动,创建的一些类、资源文件没有被使用,这类文件如果没有被检查出来,被一起打包到安装包中,无疑增加了安装的体积。

怎么检测这些冗余或无用文件?

方法一:利用代码编辑器提供的检测工具,可以检查出语法的错误、拼写的错误、资源未引用和类文件未使用等,工具能够很方便罗列出未引用的资源和未使用的类文件,可以手动移除未使用的资源或未使用的类文件,当然也可以考虑通过开发一款小工具自动删除。

方法二:同时,我们可以配合第三方检测工具,比如:腾讯的matrix,滴滴的booster,能够更灵活设定检查的参数,按照我们的需要检查冗余的文件或无用的资源。

下面我们分别看一下Android检测的结果

1.2.1 Android项目检测样例

找到为包含alpha通道的png图片

检查出未压缩的文件类型

检查出未引用的资源文件

检查出未使用的原生文件

1.3 应用程序功能定制

经常我们下载移动端的时候,会发现有极速版、定制版、完整版,为什么会有那么多个版本呢?这是为了适配不同型号的机型,一个15年的机型,内存可能只有1G,这时候安装一个几十M的应用,安装不仅缓慢甚至还卡,想必影响用户的体验。即使,用户使用4G内存的手机,极速版的功能已经满足了用户的需求,是不必要下载一个完整版,省了流量同时也减少用户手机内存的浪费。

1.3.1 功能定制

实现应用程序功能定制,首先需要追踪到用户的行为,了解用户用户常用的功能,统计排列前十的功能打包成极速版,排列前二十的功能打包成定制版,已经下载极速版、定制版的用户需要更多的功能,引导下载完整版

1.3.2 联网更新

完整版应用程序,也可以考虑组件化开发,除了首页立即可用的功能外,额外的功能可以考虑用户进入首页的时候网络下载,一些大的图片类似banner图、背景图不必全部打包,可以在应用程序启动的时候下载并缓存。

二、界面耗时检测

由于帧速率会有较大的跳动,而且以动画帧速率来说超过25帧每秒,肉眼看起来都是流畅的,所以仅根据帧速率来判断卡顿现象是不严谨的。所以参照美团Hertz监控方案,采用耗时来判断,通过设定一个时间阈值,超过阈值判断为一次卡顿。

2.1 APP启动耗时

统计启动耗时,指的是应用启动过程到进入主界面用户可以交互的时长,包含:应用程序创建、闪屏显示、登录数据加载、主页显示。现在要做的就是获取上述四个方面的耗时时长,进一步细分到具体的场景,应用程序初始化第三方框架需要多少时长,加载本地一张闪屏图片需要多少时长、登录获取用户数据需要多少时长等。希望加载的每一个功能业务都是必须的。

2.1.1 闪屏界面开始前

点击桌面图标到看到闪屏界面之前,根据UI属性创建预览窗口,完成初始化第三方框架(T1)。

2.1.2 闪屏界面开始到结束

加载本地文件资源,开启动画一直到进入主页初始化为止,可以根据需要对图片和动画进行优化,针对机型对图片尺寸、质量进行处理,内存不足可以考虑关闭动画效果(T2)。

2.1.3 后台数据加载处理

如果处于自动登录情况下,会出现在进入首页前的登录数据操作,包括网络请求、数据解析、登录信息存储等,这时候会出现数据加载耗时(T3)。

2.1.4 主页面初始化到结束

在自动登录情况下,APP进入的主页面是首页,否则默认进入的主页面是登录页,在主页开始初始化开始,一直到主页面进行数据加载,完成最后的渲染为止(T4)。

2.1.5 耗时分析

前期收集各种环境、各种机型在启动应用程序到进入主界面这个过程中T1、T2、T3、T4的耗时情况,统计T1、T2、T3、T4时间范围分布:

同时,记录启动的总耗时,T = T4截止时间 - T1开始时间,对总耗时的时间分组统计,5s以内为优,5s-10s内为一般,10s以上为差,通过总耗时评价APP启动性能好坏,评分规则如下:

启动时间 0~5s 5~10s 10s以上
评分 一般 过长

根据T耗时分布,如果启动用时过长,具体哪个阶段造成的耗时,再分别从T1、T2、T3、T4统计中分析。
统计不同机型的启动耗时情况,同时上传机型和内存的基本信息,能够做到特定机型优化处理,比如说:延迟初始化、业务场景优化。

2.2 页面加载耗时

从页面加载耗时角度来说,可以大致分为四类:页面之间的跳转、页面初始化布局、页面数据处理,页面二次渲染。而在页面加载前,存在一个页面跳转过程耗时,页面加载大致可以用下图理解,值得注意的是,页面完全加载结束时间T≠T1+T2+...,而是T=Ts(T0开始时间) + Te(T3截止时间)。

2.2.1 页面跳转耗时统计

如果在跳转新页面进行其他耗时操作,尤其是在主线程下操作,会造成一定的跳转延迟,会给人一种性能不好的错觉,所以检测这个过程的时间,有效的找到代码逻辑不适当问题。
从开始触发跳转任务开始记录耗时,到新页面将要开始绘制截止,这段时间为页面跳转耗时,可标记为T0。

2.2.2 页面初始化耗时统计

新页面开始进行初次布局,因为没有数据,所以这时候是一个代码编写的空壳,在几乎没有数据的情况下,统计编码布局可以更清楚的知道页面UI控件的使用情况。如多菜单多按键的情况下,可能存在多种创建方式,耗时统计可以反映创建方式的优劣一种。
当然这个并不是完全绝对的说法,因为数据往往会造成很大的影响,可能就要考虑到复用性和扩展性。但这个也是一个有效的检测指标,从UI初次绘制开始记录到绘制结束,可标记为T1。

2.2.3 页面数据处理耗时统计

在页面刚开始初次布局的同时数据往往也异步进行操作,如果异步开始在初次绘制后,时间会稍比初次绘制UI晚,当然也可能和页面绘制同在主线程,执行了初次绘制后再进行数据处理。不过不影响时间的统计,从开始进行数据处理开始记录到数据处理结束或者第二次页面渲染开始为结束点,页面能否快速正常展示很大一方面是由这部分耗时决定的,所以这个指标还是很关键,可标记为T2。

2.2.4 页面二次渲染耗时统计

二次渲染是由加载数据而来,说的直白就是刷新,根据数据重新定格布局。二次渲染相对初次渲染可能会多出了很多控件对象的创建,计算会更繁杂。结合页面初次绘制的情况就可以较为明确的知道UI布局方式的优劣。从页面再次绘制开始或从数据处理结束后开始记录,到显示完整的页面结束,可标记为T3。

2.2.5 页面耗时分析

界面A跳转界面B,经历了T0、T1、T2、T3阶段,这些子时间是一个参考的标准,总的时间T是指标用来对比的数据值。单个页面总的加载时间T可以实行按时间范围划分对比,形成百分数比例,如1s-3s(优)、3s-5s(良好)、5s-10s(劣)。这样可以统计在某个时间范围(如1s-3s)APP版本号、设备型号、设备系统、占比是多少。下面是样图。

三、内存性能分析

3.1 内存使用率(memPercent)

指的是当前APP使用过程中内存的动态情况。几个指标:打开应用内存使用率、剩余可用内存数、界面打开占用内存数、界面关闭释放内存数,将APP运行过程中上述的几个参数写入到数据库,在应用退出或下次打开的时候上传,根据内存占用大小幅度对机型进行划分,比如:10M~20M、20M~50M、50M~100M、>100M等(PS:划分幅度需要根据收集到数据的情况),统计每个幅度的比例:

10~20M 20~50M 50~100M >100M
32% 28% 35% 5%

3.2 CPU占用率(CPUPercent)

CPU作为手机的中央处理器,可以说是手机最关键的组成部分,所有应用程序都需要它来调度运行,资源有限。当APP因设计不当,使 CPU 持续以高负载运行,将会出现APP卡顿、手机发热发烫、电量消耗过快等等严重影响用户体验的现象,APP在运行的时候,会对应一个Task,而Task下可能有多条线程同时执行任务,每个线程都是作为利用CPU的基本单位。所以我们可以通过获取当前下,所有线程占用 CPU 的情况,来计算APP的 CPU 占用率。
当开启新的线程时候,CPU占用将会提高,所以在可每个线程开启的时候进行一次采集,尤其是在网络异步请求和数据异步加载。在Task结束后再次采集,因为对应线程可能并没有回收,造成CPU持续占用。

3.3 帧速率(FPS)

验证一个页面流畅程度的参数:60FPS,平均一帧的时长是16.667ms,超过这个时长的绘制,造成的影响是1s内绘制的帧数变少了,减少的帧数被称为:丢帧。用丢帧数(X)衡量一个应用程序的流畅度:

Best Normal Middle High Frozen
[0,3) [3,9) [9,24) [24,42) [42,∞)

统计所有页面的掉帧情况,根据掉帧数进行分组,统计每组数据,绘制成柱状图,了解不同手机运行应用掉帧情况。

页面在静态情况下,一般不会出现掉帧情况,而当中画面会出现动画效果的时候可能会出现帧数的大幅度跳动。所以安排记录的位置为出现动画的时候,比如手势的拖拽,列表的滚动,某个动画的播放等。直到动画结束记录停止,取记录中最低的帧数L,以60帧为标准,掉帧数X = 60-L。

3.4 图片占用率(picPercent)

指的是APP内所有图片在一定时间内占据内存数(P)与APP内存消耗总数(M)的比值,picPercent=P/M。

正常来说内存在界面打开关闭过程会保持动态平衡,如果出现界面打开关闭后,P处于上升的趋势,可以判断可能发生了图片内存泄露或图片还没有及时被回收,找到内存泄露的图片或没有被回收的图片是我们优化的方向。

在不同设备中,P还可以反应我们使用了哪套图片尺寸。根据mdpi、hdpi、xhdpi每套图片大小的不同,在低端设备加载合适尺寸的图片,在高端设备中加载清晰度更高的图片尺寸,达到减少图片内存消耗,提升图片显示效果。一个hdpi尺寸的设备使用了xhdpi的图片,造成的影响是图片被压缩了,显示有可能没有占满控件;一个xxhdpi尺寸的设备使用了hdpi的图片,造成的影响是图片被放大了,占据更多的内存。

3.5 图片重复数(duplicateNum)

指的是内存中Bitmap像素数数据相同的对象数(N)。获取重复对象数N,记录其引用链。实现的效果类似下图:

3.6 图片超宽率(exceedPercent)

指的是图片尺寸大于控件大小或图片尺寸大于屏幕大小的数量(I)在总图片使用数量中(N)的占比,exceedPercent=I/N。获取大于控件大小或屏幕大小的图片所在的 Activity 和堆栈,上传后台。

3.7 发生GC的次数(gcNum)

指的是触发垃圾回收器回收的次数。频繁地创建对象和回收对象,会造成内存抖动,影响用户体验。这个过程往往出现在新页面出现和页面退出或页面刷新的情况,比如在某一页面进行加载更多数据需要列表刷新,这时候将短时间内创建或销毁较多的对象。这个过程可以对系统垃圾回收次数进行统计。

3.8 对象占用率(objPercent)

指的是APP内创建对象在一定时间内占据内存数(O)与APP内存消耗总数(M)的比值,objPercent=O/M,设定objPercent一个比列分层,如0.0001%-0.005%为正常,超过0.005%为溢出。
收集的目标更多的针对频繁使用到的对象,比如单例(创建后,该类只存在一个实例对象,而且穿插了整个应用的生命周),虽然是为了节省内存资源,正因如此这个对象始终没有释放掉。在每次对象开始执行任务到该对象任务执行结束后,将对该对象进行一次占用率的检测,比如一次网络请求、一次分享。

四、SQLite质量检测

略...

五、文件IO监控

略...

六、采集方式

应用在前台的时候,开始去采集,暂定2分钟采集一次(具体还要根据测试效果而定)PSS、Java堆、图片总内存。

通过上面比例来衡量我们APP的一般内存占用情况,能够将内存降低一个级别,作为考核开发人员的指标。

资料参考:
《Android高手开发课程》
《matrix开源框架》
《DoKit支持Activity启动耗时统计方案》
《移动端性能监控方案Hertz》

你可能感兴趣的文章

来源:TeachCourse每周一次,深入学习Android教程,关注(QQ158#9359$239或公众号TeachCourse)
转载请注明出处: https://www.teachcourse.cn/2791.html ,谢谢支持!

资源分享

分类:Android 标签:
浅谈OptionMenu选项菜单 浅谈OptionMenu选项菜单
Genymotion如何添加下载过的设备? Genymotion如何添加下载过的设备
夏天适合喝冰凉的水、饮料吗?为什么喝冰凉的水反而不解渴 夏天适合喝冰凉的水、饮料吗?为
HashMap方法解析 HashMap方法解析