Android 卡顿优化 1 卡顿解析
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):
丢帧给用户的感觉就是卡顿, 而且如果运算过于复杂, 丢帧会更多, 导致界面常常处于停滞状态, 卡到爆.
那么会有哪些常见的情况会导致运算超过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, 就是在一个像素点上绘制了多次. 常见的就是:
- 绘制了多重背景.
- 绘制了不可见的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. 需翻墙
导致频繁GC有两个原因:
- 内存抖动(Memory Churn), 即大量的对象被创建又在短时间内马上被释放.
- 瞬间产生大量的对象会严重占用Young Generation的内存区域, 当达到阀值, 剩余空间不够的时候, 也会触发GC. 即使每次分配的对象需要占用很少的内存,但是他们叠加在一起会增加Heap的压力, 从而触发更多的GC.
这些GC操作可能会造成上面说到的丢帧, 如下:
就会让用户感知到卡顿了.
一般来说瞬间大量产生对象一般是因为我们在代码的循环中new对象, 或是在onDraw中创建对象等. 所以说这些地方是我们尤其需要注意的...
Android 卡顿优化 1 卡顿解析的更多相关文章
- android问题及其解决-优化listView卡顿和怎样禁用ListView的fling
问题解决-优化listView卡顿和怎样禁用ListView的fling 前戏非常长,转载请保留出处:http://blog.csdn.net/u012123160/article/details/4 ...
- Android app 性能优化的思考--性能卡顿不好的原因在哪?
说到 Android 系统手机,大部分人的印象是用了一段时间就变得有点卡顿,有些程序在运行期间莫名其妙的出现崩溃,打开系统文件夹一看,发现多了很多文件,然后用手机管家 APP 不断地进行清理优化 ,才 ...
- android中app卡顿优化问题
所谓app卡顿原因就是在运行时出现了丢帧,还可能是UI线程被阻塞.首先来一下丢帧现象,android每16ms会对界面进行一次渲染,如果app的绘制.计算等超过了16ms那么只能等下一个16ms才能 ...
- Android性能优化----卡顿优化
前言 无论是启动,内存,布局等等这些优化,最终的目的就是为了应用不卡顿.应用的体验性好坏,最直观的表现就是应用的流畅程度,用户不知道什么启动优化,内存不足,等等,应用卡顿,那么这个应用就不行,被卸载的 ...
- Android 布局渲染流程与卡顿优化
文章内容概要 一.手机界面UI渲染显示流程 二.16ms原则 三.造成卡顿的原因 四.过度绘制介绍.检测工具.如何避免造成过度绘制造成的卡顿 一.手机界面UI渲染显示流程 大家都知道CPU(中央处理器 ...
- Android 卡顿优化 4 布局优化实际技巧
今天分享一些layout布局书写中的一些技巧,希望看过之后你也一样可以写出性价比高的布局.我个人的目标是用最少的View写出一样效果的布局.因为我相信View的数量减少伴随着的就是层级的减少.从而达到 ...
- iOS应用千万级架构:性能优化与卡顿监控
CPU和GPU 在屏幕成像的过程中,CPU和GPU起着至关重要的作用 CPU(Central Processing Unit,中央处理器) 对象的创建和销毁.对象属性的调整.布局计算.文本的计算和排版 ...
- Android内存优化(二)解析Memory Monitor、Allocation Tracker和Heap Dump
前言 要想做好内存优化工作,就要掌握两大部分的知识,一部分是知道并理解内存优化相关的原理,另一部分就是善于运用内存分析的工具.本篇就来介绍内存分析工具:Memory Monitor.Allocatio ...
- Android内存优化(四)解析Memory Monitor、Allocation Tracker和Heap Dump
相关文章 Android性能优化系列 Java虚拟机系列 前言 要想做好内存优化工作,就要掌握两大部分的知识,一部分是知道并理解内存优化相关的原理,另一部分就是善于运用内存分析的工具.本篇就来介绍内存 ...
随机推荐
- webapi-1 给现有MVC 项目添加 WebAPI
1. 增加一个WebApi Controller, VS 会自动添加相关的引用,主要有System.Web.Http,System.Web.Http.WebHost,System.Net.Http 2 ...
- 数字签名(以ActiveXDemo为例)
1.创建Install.inf文件和run.bat Install.inf: [version] signature="$CHICAGO$" AdvancedINF=2.0 [Se ...
- javascript 实现图片拖动
javascript实现图片拖动效果并不难,主要的思路如下 1:给图片绑定监听鼠标按下对象,设置拖动属性为true 2:鼠标抬起:拖动属性为false 鼠标移动:改变坐标即可,新坐标=图片原始坐标+鼠 ...
- AC日记——严酷的训练 洛谷 P2430
严酷的训练 思路: 背包: 代码: #include <bits/stdc++.h> using namespace std; #define maxn 5005 int n,m,bi[m ...
- 解决ssh登录过慢问题
1.首先打开debug,看看慢在哪里 [root@BC-NFS1 ~]# ssh -v root@192.168.102.41 -p 22 OpenSSH_6.6.1, OpenSSL 1.0.1e- ...
- 部署centos6
挂载镜像和导入镜像 mount /dev/cdrom /media ll /media/ cobbler import --path=/media --name=centos6.5--arch=x86 ...
- Openstack 清除openstack网络与路由 (十七)
一)清除openstack网络与路由 “清除openstack网络与路由”和”添加openstack网络与路由”的操作步骤相反. 添加网络或路由时是先建 搭建网络>搭建子网>建立端口, 而 ...
- php jsonp跨域访问
项目中有个上传图片需要实时预览的,但又是两个系统的访问,故想了一下解决方案: 在新系统中上传图片后处理设置session,旧系统跨域访问获取对应session,进行对应模板预览. 上传图片预览按钮对应 ...
- react native android 应用状态(前端或后台)的判断
当Android应用程序被暂时放到了后台,或者又重新回到前台,是否有相应的事件可以处理到? 例如,当你的应用暂时放到了后台,是否应该做出一些操作,暂时保存界面上的数据? 可以参考:https://gi ...
- JDK Windows安装
进入至JDK下载地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html 点击下载后,会进入下载列表 点击下载后,就等 ...