安卓性能优化(响应优化)

性能优化 专栏收录该内容
2 篇文章 0 订阅

姊妹篇:性能优化(内存优化)

安卓app响应速度或使用流畅度是衡量性能的一个指标。如果一个应用用户启动应用时缓慢、使用时卡顿、甚至出现ANR那是很糟糕的体验。
通常,当应用无法响应用户输入时,系统即会显示 ANR。例如,如果应用在界面线程中执行了某些 I/O 操作(文件的读写等),导致系统无法处理传入的用户输入事件。或者,应用在界面线程中花费太多时间构建复杂的内存结构或计算游戏的下一个走法。确保高效的计算始终至关重要,但即使最高效的代码仍然需要时间来运行。
在 Android 中,应用响应性由 Activity 管理器和窗口管理器系统服务监控。当 Android 检测到以下某一项条件时,便会针对特定应用显示 ANR 对话框:

在 5 秒内对输入事件(例如按键或屏幕轻触事件)没有响应。
BroadcastReceiver 在 10 秒后尚未执行完毕。

app启动和流畅度优化

1,app启动流程

安卓系统启动后,会有一个Zygote进程,系统创建应用都是从这个进程中fork出来的,Java层入口可以查看源码中的ActivityThread类的main方法,当启动app(冷启动)时,安卓系统会从Zygote进程中fork一个新的进程分配给该应用,之后会依次创建和初始化Application类、创建MainActivity类、加载主题样式Theme中的windowBackground等属性设置给MainActivity以及配置Activity层级上的一些属性、再inflate布局、当onCreate/onStart/onResume方法都走完了后最后才进行contentView的measure/layout/draw显示在界面上( 这些在ActivityThread\ActivityManagerService\BIND机制 等配合着看下源码就可以看到,这里可以看到源码中windowmanager.addView(decorview)是在performResumeActivity之后执行,所以View的绘制是在onResume之后执行的),所以直到这里,应用的第一次启动才算完成,这时候我们看到的界面也就是所说的第一帧。所以,总结一下,应用的启动流程如下:

Application的构造器方法——>attachBaseContext()——>onCreate()——>Activity的构造方法——>onCreate()——>配置主题中背景等属性——>onStart()——>onResume()——>测量布局绘制显示在界面上。

应用在冷启动之前,要执行三个任务:

——加载启动App;

——创建App的进程;

——App启动之后立即展示出一个空白的Window;

而这三个任务执行完毕之后会马上执行以下任务:

——创建App对象;

——启动Main Thread;

——创建启动的Activity对象;

——加载View;

——布置屏幕;

——进行第一次绘制;

而一旦App进程完成了第一次绘制,系统进程就会用MainActivity替换已经展示的Background Window(可以添加logo增加用户体验),此时用户就可以使用App了。

2,使用Systrace查看问题

systrace的一个优势是可以整个手机全局的检查各个应用的进程。

2.1,生成trace文件

打开Android Device Monitor(Windows下双击SDK\tools\monitor.bat),
点击下图红圈

在这里插入图片描述
弹出如下图:
红色圈圈分别代表trace的时间(通常设置默认值5秒,并在5秒内重现问题,时间太短会导致问题重现时没有被抓到,时间太长会导致JavaHeap不够而无法保存,可能会出现无法解析的情况,因此在能抓到问题点的情况下,时间越小越好)、trace的大小(同样的,太小会导致信息丢失,时间太长会导致Java Heap不够而无法保存,建议使用默认的2048)、trace哪些TAG

在这里插入图片描述
点击OK后,执行启动app的操作。

2.2,使用Chrome打开chrome://tracing/,Load trace文件和分析

比如下图:
左侧是对应的应用ID,每个应用展开里面有Heap size、Frames、UIThread等item。
其中比较重要的两个,

Frames:
在每个App进程,都有一个Frames行,正常情况以绿色的圆点表示。当圆点颜色为黄色或者红色时,意味着这一帧超过16.6ms(即发现丢帧),这时需要通过放大那一帧进一步分析问题。对于Android
5.0(API level 21)或者更高的设备,该问题主要聚焦在UI Thread和Render Thread这两个线程当中。对于更早的版本,则所有工作在UI Thread,没有Render Thread这一项。

Alerts:
Systrace能自动分析trace中的事件,点击Alerts里的每一项,底部有显示可能的原因和建议该怎么解决。
比如对于丢帧时,点击黄色或红色的Frames圆点便会有相关的提示信息;另外,在systrace的最右侧,有一个Alerts tab可以展开,这里记录着所有的的警告提示信息。
当我们点击了Alerts或者点击右边的Alerts列表中的任何一点我们可以看到在界面的最底部会相对应的优化提示以及可能会出现优化的视频教程链接。
在这里插入图片描述
如上图,可以看到左侧对应的进程名右侧,MethodName为bindApplication的这个方法执行了792.154ms,源码中搜索bindApplication(在ActivityThread中,同样可以找到上图中activityStart、activityResume主要执行Activity的onCreate\onStart\onResume等方法),发现里面有执行Application的onCreate方法,这里就要检查是否在application的onCreate方法里初始化的东西太多,非必要的可以考虑子线程或者延后初始化。
同上面app启动流程,接下来可以看到bindApplication后面activityStart\activityResume\traversal等,可以鼠标选中+M键快速标记当前的方法下使用的时间(其它快捷键有W放大\S缩小\A左移\D右移 等,可以自己点击试试),如上图显示的bindApplication耗时792.154ms。如果发现耗时比较长的,可以到代码中查找对应的方法里是否执行了耗时之类的操作或者对应时间上的CPU、内存等的使用情况。

该文件主要是发现问题(右侧有个Alerts,提示哪些地方有可能有问题),一般和竞品生成的文件做对比,可以发现时间主要用在哪一块,具体问题点一般还要到具体的代码或者使用TraceView去检查定位到具体的方法,进而修改。

3,使用TraceView查看耗时方法

如下图:
选中进程,点击红圈的图标start method profiling,重现问题,再次点击红圈图标stop method profiling。

在这里插入图片描述
生成如下图xxx.trace文件:

在这里插入图片描述
各个参数的含义:

在这里插入图片描述
如上图所示,可以搜索具体的包名或者按照某一项的参数递增或递减排序,比如上图,获得方法执行的次数和执行总的时间。点击方法可以看到里面的各个调用方法时间分配情况,最上面还有对应的方法执行时间刻度和所在线程(滚动鼠标可以放大和缩小),进而定位到具体的方法,修改解决问题。

4,项目中一些具体操作:

1,如果想在systrace抓取的trace文件里有自己的方法,可以使用Trace.beginSection(“sectionName”) +Trace.endSection()(这个需要在生成trace文件时选中进程,否则不会被抓取到),如果是系统Rom可以使用隐藏API Trace.traceBegin(long traceTag, String methodName)+traceEnd(long traceTag)。

2,使用“设置 => 开发者选项 => 调试 GPU 过度绘制 => 显示过渡绘制区域”,根据屏幕显示的不同颜色来区分是存在过度绘制,从而排查该界面的 xml 文件,去除不必要的嵌套和background。比如使用ViewStub\merge等。当然也可以使用Layout Inspector查看界面布局情况,进行去除多余层级和负责布局的优化。

3,也可以使用Hierarchy View(不过要使用老版本的或者使用开发机,否则在WindowManageService.java的startViewServer返回false,因为[ro.secure]:[1],[ro.debuggable]: [0],这个可以adb shell getprop | findstr ro.查看),查看各个view的加载时间,并有针对的优化,比如自定义View是否在draw里面执行了比较重的操作或者实例化了比较多或者大的占资源的对象。

4,application里实例化的项尽量子线程、延迟加载或懒加载,将Activity的第一个页面的内容分页加载,甚至只加载前两三项,达到快速加载的目的。当然这个也和系统有关系,所以可以使用一个空的Activity demo查看下启动的时间,可以在Main log里搜索到Displayed com.xx.xx.MainActivity: +279ms表示这个空的Activity启动耗时,以这个作为这个手机的参考参数。

5,对于短时间大量执行的方法,非必须的话就不使用synchronized,因为这个也会增加执行时间和资源开销。

6,避免短时间创建大量临时变量导致GC,出现卡顿,可以考虑使用pool。

7,避免主线程执行比较重的磁盘 I/O 操作或者比较频繁的耗时操作(这甚至就会直接阻塞应用的执行),对于这类必须要执行的耗时操作,可以放到子线程中执行,并且尽可能减少执行时间(比如对数据库的批量操作)。

8,避免大量的多重循环等等。

好了好了,目前就记得这些了,等后面有新的再加上来。
希望疫情早点结束,大家都健健康康的,一切恢复正常。

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

weixin_小栓

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值