当前位置: 首页 > news >正文

安卓应用性能调试和优化经验分享

安卓绿色联盟应用性能标准主要基于主观体验、资源消耗和应用质量三个方面进行制定。

1、主观体验

主观体验主要是对应用启动时间和界面帧率制定标准,要求应用在视觉上足够流畅。其中应用启动时间又分为冷启动时间和热启动时间。

安卓绿色联盟性能标准要求,应用冷启动时间需小于1000ms,热启动时间需小于500ms;普通应用帧率应大于55fps,游戏视频帧率应大于25fps。

2、资源消耗

资源消耗主要是要求应用不能占用过高的内存和CPU。

安卓绿色联盟性能标准要求应用前台内存占用应小于500M,后台内存占用应小于400M;在CPU占用方面要求应用在后台灭屏5分钟后,CPU占用不超过2%。

3、应用质量

应用质量主要是对应用过度绘制和内存泄露的情况作出要求,规定应用不能存在过度绘制和内存泄露。

安卓绿色联盟性能标准要求应用界面任意像素点不存在4x的绘制的情况, 3x绘制的区域不能超过界面面积的1/3,在Strict Mode中不允许有红框闪烁。

性能调试方法

在了解性能调试方法之前,我们可以先通过下图了解安卓应用性能与系统之间的关系。一个应用从应用绘制到最终显示在LCD上经历了一个漫长的路径,在任何一个阶段出现延时都会导致界面上的卡顿。

image

1、Strict Mode

Strict Mode意思为严格模式,是Android提供的一种运行时检测机制,一般用来检测在主线程发生的耗时动作,比如IO读写、数据库操作、复杂算法等。在手机设置开发者选项把Strict Mode打开,就可以在界面上把它打开了。

严格模式主要有2个策略,一个是线程策略,即ThreadPolicy,主要检测主线程中的一些耗时操作;另一个是虚拟机策略,即VmPolicy,主要检测一些对象的泄漏。

两大策略检测的内容和开启方法可以依据下图中的说明进行使用。

image

严格模式有三种惩罚模式:应用崩溃、弹窗警告和打印日志。在性能测试中,我们可以通过APPLogcat抓取Strict Mode的日志,同时利用代码启用Strict Mode,配合我们所需要的策略和惩罚,就可以及时定位应用的违规细节,并及时进行性能优化。

当我们碰到违规的行为时,该如何进行治理呢?建议将文件操作放到工作线程去完成,如果在主线程上提及操作,建议使用Apply和Commit去完成。如果存在对象未关闭的情况,可以通过对应的StackTrace进行关闭。

2、OverDraw DeBugger

Overdraw是指屏幕上的某个像素在同一帧的时间内被绘制了多次,这个工具使用色块来代表不同数量的过度绘制,我们可以使用这个工具来定位由过度绘制引起的用户界面卡顿问题。

在开发者选项中选择开启 Debug GPU Overdraw选项,即可在安卓设备上将过度绘制问题可视化。

image

左图为正常模式下显示的视图,右图为开启GPU Overdraw后显示的视图

3、Profile GPU Rendering

ProfileGPU Rendering 工具以滚动直方图的形式直观地显示渲染界面窗口帧所花费的相对时间(以每帧 16 毫秒的速度作为对比基准)。这个工具同样也是在安卓设备的开发者选项中开启。每个管线的高度表示时间,管线中各个彩色区段代表不同含义。

下表介绍了使用运行Android 6.0及更高版本的设备时分析器中不同竖条区段的含义。

image

4、Android Profiler

Android Profiler是一个Android Studio集成的应用性能分析器,可以实时查看CPU、Memory和Network的动态情况。以下重点介绍CPU Profiler:

CPU Profiler 可帮助您实时检查应用的 CPU 使用率和线程 Activity,并记录函数跟踪,方便大家优化和调试应用代码。

当打开 CPU Profiler 时,它将显示应用的 CPU 使用率和线程 Activity。

image

CPU Profiler可以选择不同的标签,并对应用线程进行跟踪。如:

(1)Flame Chart标签会提供一个倒置的调用图表,汇总相同的调用堆栈,收集调用顺序完全一致的函数,并在火焰图中用一个较长的横条表示它们。

(2)Top Down标签能够提供每个函数调用上所花费的CPU时间。Self表示函数调用在执行自己的代码上所花的时间;Children表示函数调用子方法所花费的时间;Total表示Self和Children时间的总和。

5、Systrace

Systrace是我们分析性能最常用的工具之一,它可以分析整机系统性能及动态场景的性能问题。

Systrace 允许您在系统级别收集和检查设备上运行的所有进程的计时信息。它将来自Android内核的数据(例如CPU调度程序,磁盘活动和应用程序线程)组合起来,以生成HTML报告。

image

上图左部是Systrace的界面,我们可以通过右边的代码抓取Systrace,观察进程的执行时间。在输入抓取命令时,时间参数一般选择5到10秒,因为时间过短可能会抓不到想要的数据,时间过长则可能抓取失败。

一般我们通过Chrome浏览器查看生成的trace文件,也可以通过DDMS图形界面去抓取Systrace。

image

拿到一个Systrace时主要考察哪些因素?首先看一下CPU的频率,找到对应的进程或者线程,查看相关信息;同时还要观察GPU的频率、Surface Flinger还有绘图的Buffer状态等。

当应用发生卡顿时,我们可以通过Systrace进行分析。在生成的trace文件中,找到主线程UI,每一帧都会标记一个带有F的圆形。当原型为绿色时,代表页面流畅,而黄色和红色则存在超时,我们可以点击去查看具体存在什么问题。

性能案例分析

案例1:界面滑动卡顿

image

从图中可以看到,这是一个手动滑动事件,当deliverInput事件发生后,第一帧就发生了卡顿。从systrace看UI thread执行draw的时间相当耗时导致丢帧卡顿,而且大部分时间都在做decodeBitmap,共耗时99.045ms。这时,我们打开applog发现,有StrictMode相关的错误提示,从中可以定位到耗时函数。

image

从上图我们看出有一个网络访问违规,大概可以推测应用在从网络上下载了一个数据流,数据流里可能包含了一些图形,通过decodeBitmap把它解析出来展示在UI界面中。正产情况下,我们应该把网络访问放在工作线程里面去处理,将数据下载完了之后再放到主线程中去展示,避免这种问题的发生。

案例2:Strict Mode错误提示

image

从上图Strict Mode的日志可以看出:StrictMode policy violation耗时2秒左右。通过最下行蓝色的log,可以知道应用是在某一个目录里面寻找一个文件,判断文件是否存在。

面对这种问题,我们应该把IO操作放到工作线程。正常情况下IO的发生非常快,但是在系统繁忙时,IO放在主线程会产生较大的问题,因为它要等别的程序读写完成之后,才会下发,产生超时。

案例3:GPU调用不当导致的卡顿问题

image

这是一个GPU的例子,上图主要问题是GPU使用了太长时间处理应用传过来的buffer,例子中Surfaceflinger 使用GPU 做了图像叠加,说明图层比较多。使用GPU做叠加主要会产生功耗和唤醒耗时的问题。大家在做界面设计的时候,尽量不要使用GPU进行叠加。在上面的例子中,GPU叠加之后,导致了大概15ms左右的延时,因为GPU操作完成以后还需要交给Surfaceflinger把图像显示到屏幕上。

案例4:CPU调用不当导致的界面滑动卡顿问题

image

可以通过上图的红色条块了解messageloop RunTask信息,红色条块上的蓝色bar,表示线程在CPU上的状态。蓝色表示这个线程处于等待CPU调度的状态,可见等待超过8ms的时间,是正常调度周期好几倍。导致这种情况发生的原因有两个:CPU负载过大或CPU调度出现了问题。在上图中我们可以看出,CPU0和CPU1使用率100%,但是CPU2和CPU3是offline的状态,说明系统出现问题,导致CPU2和CPU3未能唤醒,帮助完成系统任务。

性能优化建议

1、避免内存泄露

在应用开发过程中,首先要避免内存泄露的问题,内存泄露是一种比较严重的性能问题,在安卓绿色联盟应用性能标准中也要求应用不允许发生内存泄露。

下图是常见的内存泄露防范方法和内存泄露检测工具。

image

2、避免不良设计或程序算法导致CPU占有率持续偏高

主要业务处理分散到不同线程,便于后续利用多核处理器的并行处理能力,避免一核累死,7核围观;
使用top命令观察应用线程的CPU占有率,找出高负载的进程进行分析,并针对优化。

3、避免OnXXX 回调函数中进行耗时操作,避免主线程卡顿

Android系统中正常情况下所有onXXX类函数均运行在主线程中。

image

在上图中,我们可以看到两帧中间有一个因为接收广播处理导致的158ms的卡顿。在这些函数中,我们应该避免网络通信操作、文件读写操作、数据库数据改动的操作、图形处理、文本分析等操作,将这些工作尽可能的移到工作线程中去,从而避免主线程卡顿。

4、合理使用系统资源

合理使用系统资源主要指的是软资源。下图是对广播资源调用的一些建议。

image

关注安卓绿色联盟公众号,回复关键词“23”,获取PPT。

相关文章:

  • Redis 懒删除(lazy free)简史
  • vue全家桶+Koa2开发笔记(8)--开发网页
  • Mycat - 实现数据库的读写分离与高可用
  • 理解 JavaScript 中的 this
  • 块级、内联、内联块级
  • Spring Boot 运作原理
  • 虚拟机网络不通故障解决
  • 原型模式深入探讨-【设计模式4】
  • 撕掉普通程序员的标签,这才是真正的大数据工程师!
  • Spring基于XML方式配置事务
  • 程序员最讨厌的9句话,你可有补充?
  • 一些关于Rust在2019年的思考
  • 通信类
  • 力扣(LeetCode)56
  • mybatis-plus 3.1.0 发布,划重点一大波升级
  • 9月CHINA-PUB-OPENDAY技术沙龙——IPHONE
  • @angular/forms 源码解析之双向绑定
  • [译]前端离线指南(上)
  • Akka系列(七):Actor持久化之Akka persistence
  • ERLANG 网工修炼笔记 ---- UDP
  • IndexedDB
  • JavaScript/HTML5图表开发工具JavaScript Charts v3.19.6发布【附下载】
  • Java教程_软件开发基础
  • php面试题 汇集2
  • python 学习笔记 - Queue Pipes,进程间通讯
  • RedisSerializer之JdkSerializationRedisSerializer分析
  • REST架构的思考
  • TypeScript迭代器
  • vue总结
  • 阿里云ubuntu14.04 Nginx反向代理Nodejs
  • 分布式熔断降级平台aegis
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 通过几道题目学习二叉搜索树
  • 小程序开发中的那些坑
  • 携程小程序初体验
  • 再谈express与koa的对比
  • 容器镜像
  • ​ssh免密码登录设置及问题总结
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • (06)Hive——正则表达式
  • (Arcgis)Python编程批量将HDF5文件转换为TIFF格式并应用地理转换和投影信息
  • (NSDate) 时间 (time )比较
  • (Redis使用系列) Springboot 使用redis实现接口幂等性拦截 十一
  • (ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY)讲解
  • (附源码)springboot 个人网页的网站 毕业设计031623
  • (译) 理解 Elixir 中的宏 Macro, 第四部分:深入化
  • (正则)提取页面里的img标签
  • (转)Linux整合apache和tomcat构建Web服务器
  • (转)拼包函数及网络封包的异常处理(含代码)
  • *ST京蓝入股力合节能 着力绿色智慧城市服务
  • .net core Swagger 过滤部分Api
  • .net core webapi 大文件上传到wwwroot文件夹
  • .net 写了一个支持重试、熔断和超时策略的 HttpClient 实例池
  • .NET面试题(二)
  • .net使用excel的cells对象没有value方法——学习.net的Excel工作表问题