Android App 优化之消除卡顿

转载:http://gold.xitu.io/post/582583328ac247004f3ab124

1, 感知卡顿

用户对卡顿的感知, 主要来源于界面的刷新. 而界面的性能主要是依赖于设备的UI渲染性能. 如果我们的UI设计过于复杂, 或是实现不够好, 设备又不给力, 界面就会像卡住了一样, 给用户卡顿的感觉.

1.1 16ms原则

在剖析卡顿的原因之前, 我们先来了解下Android中著名的"16ms"原则:

Android系统每隔16ms会发出VSYNC信号重绘我们的界面(Activity).
为什么是16ms, 因为Android设定的刷新率是60FPS(Frame Per Second), 也就是每秒60帧的刷新率, 约合16ms刷新一次.

就像是这样的:

这就意味着, 我们需要在16ms内完成下一次要刷新的界面的相关运算, 以便界面刷新更新. 然而, 如果我们无法在16ms内完成此次运算会怎样呢?

例如, 假设我们更新屏幕的背景图片, 需要24ms来做这次运算. 当系统在第一个16ms时刷新界面, 然而我们的运算还没有结束, 无法绘出图片. 当系统隔16ms再发一次VSYNC信息重绘界面时, 用户才会看到更新后的图片. 也就是说用户是32ms后看到了这次刷新(注意, 并不是24ms). 这就是传说中的丢帧(dropped frame):

dropped frame

丢帧给用户的感觉就是卡顿, 而且如果运算过于复杂, 丢帧会更多, 导致界面常常处于停滞状态, 卡到爆.

那么会有哪些常见的情况会导致运算超过16ms, 进而丢帧, 让用户觉得卡顿呢?

2, 卡顿原因分析及处理

一般来说, 会有以下几种情况导致卡顿这种性能问题, 我们逐一看下:

2.1 过于复杂的布局

上节有说, 界面性能取决于UI渲染性能. 我们可以理解为UI渲染的整个过程是由CPU和GPU两个部分协同完成的.

其中, CPU负责UI布局元素的Measure, Layout, Draw等相关运算执行. GPU负责栅格化(rasterization), 将UI元素绘制到屏幕上.

如果我们的UI布局层次太深, 或是自定义控件的onDraw中有复杂运算, CPU的相关运算就可能大于16ms, 导致卡顿.

这个时候, 我们需要借助Hierarchy Viewer这个工具来帮我们分析布局了. Hierarchy Viewer不仅可以以图形化树状结构的形式展示出UI层级, 还对每个节点给出了三个小圆点, 以指示该元素Measure, Layout, Draw的耗时及性能.

具体请参考App优化之Layout怎么摆.

2.2 过度绘制(Overdraw)

上节说的CPU方面的, 关于GPU的绘制, 如果我们的界面存在Overdraw, 也可能导致卡顿.

Overdraw: 用来描述一个像素在屏幕上多少次被重绘在一帧上.
通俗的说: 理想情况下, 每屏每帧上, 每个像素点应该只被绘制一次, 如果有多次绘制, 就是Overdraw, 过度绘制了.

2.2.1 调试Overdraw

Android系统提供了可视化的方案来让我们很方便的查看overdraw的现象:
在"系统设置"-->"开发者选项"-->"调试GPU过度绘制"中开启调试:

此时界面可能会有五种颜色标识:

  • 原色: 没有overdraw
  • 蓝色: 1次overdraw
  • 绿色: 2次overdraw
  • 粉色: 3次overdraw
  • 红色: 4次及4次以上的overdraw

一般来说, 蓝色是可接受的, 是性能优的.

2.2.2 Overdraw的分析处理

上面有言, 所谓Overdraw, 就是在一个像素点上绘制了多次. 常见的就是:

  1. 绘制了多重背景.
  2. 绘制了不可见的UI元素.

还是以GithubApp这个App的代码为例调试, 打开应用, 展示是这样的:

可以看到是中间列表这块overdraw比较严重. 查看代码发现:

fragment_trending_container.xml中ViewPager设置了背景:

<android.support.v4.view.ViewPager
    android:id="@+id/view_pager"
    android:background="@color/md_white_1000"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

而ViewPager中的fragment又设置了背景:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/refresh_layout"
    android:background="@color/md_white_1000"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    ...
</android.support.v4.widget.SwipeRefreshLayout

完整代码请查看Github上源码, 本文分析时commit截止到b01b5793.

删除外层ViewPager的背景再看:

可以发现中间列表区域已经不再是红色了, 但是也没有达到蓝色这个可以接受的层级. 这是因为我们的Activity默认情况下, theme会给window设置一个纯色的背景. 因为我们这里不想使用这个默认的背景,故而给layout加了一层背景, 导致了多重绘制背景.

当然我们也可以自定义主题, 将theme的window background设置成我们想要的, 而不在布局中设置.

可以通过如下方式去掉window的背景.

设置主题:

<item name="android:windowBackground">@null</item>

或是代码设置, 在onCreate中:

getWindow().setBackgroundDrawable(null);

此时我们看到的效果:

已基本达到优化水平.

以上旨在提供分析方法和思路.
Overdraw主要原因是背景的多重绘制, 或是不可见的View在背后绘制等, 但不仅限于此.

2.3 UI线程的复杂运算

上文ANR相关分析中就说到UI线程的复杂运算会造成UI无响应, 当然更多的是造成UI响应停滞, 卡顿.

产生ANR已经是卡顿的极致了, 具体分析可以参看App优化之ANR详解一文.

关于运算阻塞导致的卡顿的分析, 可以使用Traceview这个工具.
具体Traceview的介绍, 以及实战分析, 可以参考App优化之提升你的App启动速度之理论基础App优化之提升你的App启动速度之实例挑战.

在这里需要提下我们在性能分析工具中提到的StrictMode.

2.3.1 StrictMode的使用

StrictMode用来基于线程或VM设置一些策略, 一旦检测到策略违例, 控制台将输出一些警告,包含一个trace信息展示你的应用在何处出现问题.

通常用来检测主线程中的磁盘读写或网络访问等耗时操作.

在Application或是Activity的onCreate中开启StrictMode:

 public void onCreate() {
     if (BuildConfig.DEBUG) {
         // 针对线程的相关策略
         StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                 .detectDiskReads()
                 .detectDiskWrites()
                 .detectNetwork()   // or .detectAll() for all detectable problems
                 .penaltyLog()
                 .build());

         // 针对VM的相关策略
         StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                 .detectLeakedSqlLiteObjects()
                 .detectLeakedClosableObjects()
                 .penaltyLog()
                 .penaltyDeath()
                 .build());
     }
     super.onCreate();
 }

如果你的线程出了问题, 控制台会有警告输出, 可以定位到代码.
相对简单, 在此就不多废话了.
解决UI线程的耗时操作方案, 可以参考ANR详解里面说到的那些线程模式.

2.4 频繁的GC

上面说的都是处理上的, CPU, GPU相关的. 实际上内存原因也可能会造成应用不流畅, 卡顿的.

说到这, 想起当年配台式机的三大件(CPU, 内存, 显示器)了. 貌似分析App性能也是这几大件啊 :)

为什么说频繁的GC会导致卡顿呢?
简而言之, 就是执行GC操作的时候,任何线程的任何操作都会需要暂停,等待GC操作完成之后,其他操作才能够继续运行, 故而如果程序频繁GC, 自然会导致界面卡顿.

以下内容参考自Android Performance Patterns:Memory Churn and Performance. 需FQ

导致频繁GC有两个原因:

  • 内存抖动(Memory Churn), 即大量的对象被创建又在短时间内马上被释放.
  • 瞬间产生大量的对象会严重占用Young Generation的内存区域, 当达到阀值, 剩余空间不够的时候, 也会触发GC. 即使每次分配的对象需要占用很少的内存,但是他们叠加在一起会增加Heap的压力, 从而触发更多的GC.

这些GC操作可能会造成上面说到的丢帧, 如下:

就会让用户感知到卡顿了.

一般来说瞬间大量产生对象一般是因为我们在代码的循环中new对象, 或是在onDraw中创建对象等. 所以说这些地方是我们尤其需要注意的...

时间: 2024-10-06 15:48:36

Android App 优化之消除卡顿的相关文章

Android App优化之ANR详解

引言 背景:Android App优化, 要怎么做? Android App优化之性能分析工具 Android App优化之提升你的App启动速度之理论基础 Android App优化之提升你的App启动速度之实例挑战 Android App优化之Layout怎么摆 Android App优化之ANR详解 Android App优化之消除卡顿 Android App优化之内存优化 Android App优化之持久电量 Android App优化之如何高效网络请求 App优化系列已近中期, 前面分

Android App优化之延长电池续航时间

禁用广播接收器 确保广播接收器在真正需要时才执行指令,在onResume中其中广播接收器,在onPause中禁用, 在manifest文件中声明广播接收器时,事先默认配置成禁用的 <receiver android:name=".BatterReceiver" android:enabled="false"> 网络 1. 后台数据,后台传输数据时,判断ConnectivityManager的getBackgroundDataSetting()的返回值,如

【凯子哥带你学Android】Andriod性能优化之列表卡顿——以“简书”APP为例

这几天闲得无聊,就打开手机上的开发者模式里面的"GPU过度绘制"功能,看看别家的App做的咋样,然后很偶然的打开了"简书",然后就被它的过度绘制惊呆了,于是写了这篇性能分析的文章,从一个只有APK文件的角度,说下如何寻找布局中可能存在的性能问题,以及解决方案.本文章以简书Android最新版本1.9.1进行分析. GPU过度绘制 Hierarchy View SysTrace TraceView 总结 分析资源下载 GPU过度绘制 首先打开下面两个功能开关 开发者模

Android app优化之导致app 卡顿慢的直接原因

大多数用户感知到的卡顿等性能问题的最主要根源都是因为渲染性能.从设计师的角度,他们希望App能够有更多的动画,图片等时尚元素来实现流畅的用户体验.但是Android系统很有可能无法及时完成那些复杂的界面渲染操作.Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需要的60fps,为了能够实现60fps,这意味着程序的大多数操作都必须在16ms内完成()时间超出16ms越多,丢的帧就越多,可以大概估计一下Android 5秒没响应抛

Android App优化建议(转载)

假如要Google Play上做一个最失败的案例,那最好的秘诀就是界面奇慢无比.耗电.耗内存.接下来就会得到用户的消极评论,最后名声也就臭了.即使你的应用设计精良.创意无限也没用. 耗电或者内存占用等影响产品效率的每一个问题都会影响App的成功.这就是为什么在开发中确保最优化.运行流畅而且不会使Android系统出问题是至关重要的了.这里不需要讨论高效编程,因为我们不会关心你写的代码是否能够经得起测试.即使高效的代码也是需要时间来运行.今天这篇文章我们就讲讲怎么尽可能地缩短运行时间,以及如何开发

优化phpstorm运行卡顿问题!

在PHPSTORM中点击导航菜单:Help -> Edit Custom VM Options 如果是第一次点击,会提示是否新建配置文件,点击“是” 在弹出的编辑框末尾加上以下配置 -Dawt.usesystemAAFontSettings=lcd-Dawt.java2d.opengl=true Dawt.java2d.opengl的作用是如果硬件加速已经被开启,可以通过这个选项来提高Swing GUI速度,默认值为false 参考: 解决phpstorm运行很卡问题! http://blog.

android APP优化知识图谱

一.大纲 二.看大图 http://img.blog.csdn.net/20150719151037884 三.详情 版权声明:本文为博主原创文章,未经博主允许不得转载.

Android内存优化1 了解java GC 垃圾回收机制3

引言 接App优化之内存优化(序), 作为App优化系列中内存优化的一个小部分. 由于内存相关知识比较生涩, 内存优化中使用到的相关工具, 也有很多专有名词. 对Java内存管理, GC, Android内存管理, Dalvik/ART等知识有一个理论的认识, 可以让我们更好的使用这些工具, 分析内存问题. 据此, 我们就先从理论入手, 聊聊GC那些事儿. 1, 何为GC GC 是 garbage collection 的缩写, 垃圾回收的意思. 也可以是 Garbage Collector,

Android App 性能优化系列结语篇

关于Android App的优化, 从第一篇的计划开始, 到内存优化的系列文结束, 不知不觉近三个月的时间, 写了十五六篇相关的博文, 算是对自己的知识的一个系统化, 也希望能给大家一些帮助.在此有对此做一个总结. 路线Android App优化1, App性能优化要怎么做在系列的开篇文中, 我们聊到了本系列的一个缘由, 和当时的一个计划, 系列也基本是朝着这个这个方向走的.2, 性能分析工具在此介绍了一些惯用的性能分析工具, 包括官方, 第三方的, 内存分析的, UI分析的, 执行时间性能分析