布局的设计和编码影响是多方面的,包括界面启动,交互相应,内存,GC
整体布局思路:要求layout呈现扁平化,宽而浅的效果。
基本要求:
1. 界面layout框架绘制控制在1次以下
2. 大容器ViewGroup控制在3次绘制
3. View控制4x次的绘制
4. 一般主页等重度页4x绘制控制在整个layout的1/4,轻度页3x绘制控制1/4并且无4x绘制。
备注:过渡绘制指打开系统设置开发者模式过渡绘制选项,蓝色(1x次绘制)-》浅绿色(2x绘制)-》紫色/淡红色(3x绘制)-》红色(4x绘制)
根据业务的不同与所需要的测试粒度的不同,就会有不同的检测维度,主流面采用如下:
1. 界面过度绘制(检测过度绘制)
2. 渲染性能(抓取UI渲染性能呈现,尤其是自定义控件)
3. 布局边界合理性(检测元素显示的合理性)
1. 视图不必要的相互重叠、窄而深的设计
2. 有不必要的多层背景(Theme、Activity、ViewGroup、selector)
3. 不必要立即渲染的控件
检测和解决界面性能问题很大程度上依赖于应用程序UI设计和layout架构,Andorid提供了很多调试工具,常见的界面性能调试工具如下:
基本功能:检测过渡绘制结果
说明:关注纵向层叠
打开方式:设置-》开发者选项-》调试GPU过度绘制-》显示过度绘制区域
示例:略
基本功能:检测具体layout设计
说明:关注横向重叠
打开方式:设置-》开发者选项-》显示边界
示例:略
基本功能:检测布局层级,抓取渲染耗时重灾区
打开方式:Eclipse或Studio中打开Android Device monitor
说明:该工具的兼容性和成功率有较大问题,可尝试配合使用
示例:图中4为onDraw()等执行情况,有3个颜色,绿色,黄色,红色,红色代表着耗时最长。
基本功能:检测布局以及xml代码,提供优化建议
打开方式: Studio2.2中打开当前xml文件,Analyze-》inspect code-》current file
说明:如果还没有迁移到studio可以针对性单文件使用
示例:如图直接提示多余的一个FrameLayout
基本功能:检测布局,编码期实时调试边界和层级
打开方式:Studio2.2中xml文件编辑菜单
说明:如果还没有迁移到studio可以针对性单文件使用
示例:如图
基本功能:检测布局层级
打开方式:Studio2.2中Android Monitor-》layout inspector
说明:兼容性有问题,尝试配合使用
示例:如图
基本功能:追踪渲染性能,帧画面渲染的耗时
打开方式:开发者选项-》 GPU 呈现模式分析-》在屏幕上显示为条形图,兼容性有问题,可尝试配合使用
说明:
这个工具会在Android 设备的屏幕上实时显示当前界面的最近 128 帧的 GPU 绘制图形数据,中间有一条绿线,代表 16 ms ,保持动画流畅的关键就在于让这些垂直的柱状条尽可能地保持在绿线下面。
每一个柱状图都是由三种颜色构成:蓝、红、黄。
蓝色代表的是这一帧绘制 DisplayList 的时间。通俗来说,就是记录了需要花费多长时间在屏幕上更新视图。用代码语言来说,就是执行视图的 onDraw 方法,创建或更新每一个视图的 Display List 的时间。
红色代表的是这一帧 OpenGL 渲染 DisplayList 所需要的时间。通俗来说,就是记录了执行视图绘制的耗时。用代码语言来说,就是 Android 用 OpenGL ES 的 API 接口进行 2D 渲染 Display List 的时间。
黄色代表的是这一帧 CPU 等待 GPU 处理的时间。通俗来说,就是CPU 等待 GPU 发出接到命令的回复的等待时间。用代码语言来说,就是这是一个阻塞调用。
示例:略
基本功能:使用类似上述追踪渲染性能,但是同样兼容性有问题,可尝试配合使用
打开方式:Studio中Android Monitor-》Monitors-》GPU
最直接的影响为GC、OOM、APP本身的缓存和快速加载等性能
备注:Android4.4引进了新的 ART 虚拟机来取代 Dalvik 虚拟机。Dalvik 虚拟机的 GC 是非常耗资源的,并且在正常的情况下一个硬件性能不错的Android设备也会很容易耗费掉 10 – 20 ms 的时间,因此一旦发生GC,基本都会有所卡屏;ART则可将GC耗时锐减到5ms左右,但滚屏等大量渲染途中发生GC,依然容易卡屏。
整体内存思路:
1. 严控非图片缓存的内存,稳控图片缓存
2. 分散峰值碰撞
3. 禁止内存抖动,严防泄露
4. 尽可能及时主动回收资源
基本要求:
1. 均值控制在可用内存的2/3
2. 分页增长控制在10M+-5(视分页资源重度)
3. 峰值不发生OOM
4. 无内存抖动
5. 基本无泄露(偶现泄露控制在5M以下)
根据模块和业务的不同,主流面采用如下:
1. Monkey随机2h以上的内存表现
2. 人工冒烟单模块10min以上内存表现
3. 一般操作结合不同加载环境下内存表现
1. 内存过高,无法释放,甚至OOM
2. 频繁的高峰
3. 抖动
4. 泄露
检测和解决界面内存问题,Andorid和第三方提供了很多调试手段,包括静态检测工具和动态代码分析工具。
基本功能:内存实时监控,直观定位当前虚拟机进程实时内存变动
打开方式:Studio中Android Monitor-》monitors-》Memory
说明:吃电脑内存,对studio日常工作有影响,不用时,请折叠关闭或者左上角暂停。
示例:如图
基本功能:内存实时数据抓取,也可以抓到一些重要Objects,比如Activities,可以看到是否正常销毁;轻量级。
打开方式:adb shell(轻快),或者左侧菜单System Information(稍重)
说明:按需使用
示例: adb shell dumpsys meminfo –a [packagename],
基本功能:检测当前实时Java Heap,并可以计算泄露的Activity及其reference tree。
打开方式:Studio Monitor-》Memory-》Dump Java Heap
说明:按需实时使用
示例:
该示例可以直接抓取到泄露的Activity,并分析出简单的泄露问题,如此mContext被FileInstallUtils持有;
但是Activity有没有最终泄露可以多抓取几次进行确认,对于重度的Activity,一般认为1.5min内需要完成回收。
官方地址:http://www.eclipse.org/mat/
基本功能:检测内存文件,评估大对象,评估泄露引用关系
打开方式:将3中Studio抓到的hprof文件进行转换,然后用MAT打开,
转换方式:
1,在Captures面板,右键hprof文件,有菜单export to standard .hprof
2, 进入SDK这个工具 hprof-conv,hprof-convxxx-a.hprof xxx-b.hprof
说明:studio无法完成泄露分析时,深度使用
示例:
MAT功能多且强大,公开使用文档较多,这里示例一个quick start方式-Histogram查询
1, 点击 Actions下的 Histogram项将得到 Histogram结果:
2,点击表头可进行排序,在表的第一行可以输入正则表达式来匹配结果,比如输入你想检测的Activity的SimpleName,注意大小写一致
3,在某一项上右键打开菜单选择 listobjects ->with incoming refs 将列出该类的实例:
它展示了对象间的引用关系,比如展开后的第一个子项表示这个 HomePage(0x420ca5b0)被HomePageContainer(0x420c9e40)中的 mHomePage属性所引用
4,快速找出某个实例没被释放的原因,可以右健 Pathto GC Roots-->exclue all phantom/weak/soft etc. reference
得到的结果是:
从表中可以看出 PreferenceManager -> … ->HomePage这条线路就引用着这个 HomePage实例。用这个方法可以快速找到某个对象的 GC Root,一个存在 GCRoot的对象是不会被 GC回收掉的。
从右边的heap栏目也可以看到引用对象的大小。
基本功能:实时追踪一段时间内的内存调用情况,可按线程分类展开,studio开发还可以直接跳转到source code。
打开方式:Studio中Android Monitor-》Monitors-》Memory-》AllocationTracking
示例:
基本功能:实时追踪内存泄露,直接运行在终端
打开方式:
-》Github地址:
https://github.com/square/leakcanary
-》Sample-Studio:
https://github.com/liaohuqiu/leakcanary-demo
-》中文guide:
https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/
-》一个Sample-Eclipse
https://github.com/teffy/LeakcanarySample-Eclipse
APP的开启,各分页的跳转等待,甚至ANR
基本思路:
1. 理清启动任务,减少主线程的负担
2. 充分利用异步方式整合初始化任务
3. 优先加载界面layout,懒加载重度ViewGroup和非必须View
4. 优先加载静态数据,懒加载重度数据和实时网络数据
基本要求:
1. APP启动无明显黑屏,界面间跳转无明显闪屏
2. 冷启动application到广告页不超过1s,启动主页重度页不超过1s,分页不超过500ms
3. 严格红闪不超过一次
备注:
Activity启动时间,有许多参考统计方法,精度和区间也有所差异,这里采用比较易用的方式,使用adb抓取ActivityManager的displayed时间值;
对于application的启动到第一页,参考下述日志方法;
严格红闪,参考系统设置-》开发者模式-》打开严格模式,主线发生程耗时事件时屏幕闪烁。
根据模块和业务的不同,主流面采用如下:
1. 冷启动APP
2. 热启动跳转分页
3. 分页返回上级页面
1. 启动APP黑屏
2. 启动时间主页、分页过长
3. 分页返回上级闪屏
检测和解决界面启动问题,Andorid提供了一些调试手段,包括静态检测工具和动态代码分析工具,因为系统的碎片化和较复杂启动机制等原因,各种手段检测的结果存在一定差异,但是启动优化的目标是无上限的,所以检测值为参考和定位,作尽可能的优化。
基本功能:检测当前Application、Activity启动和渲染的时间
打开方式:在logcat中过滤该系统日志的tag即可
说明:超轻量级,简单易用,精确性在可接受范围
示例:
12-10 15:46:31.850 3406-3435/?I/ActivityManager:
Displayedcom.example.tfguo.myapplication/.MainActivity: +407ms
这个时间包括了从启动Activity到第一次布局与绘制的所有时间。这基本上是你需要知道的主要时间。它不包含用户点击app图标然后系统开始准备启动activity的时间,开发者无法影响这个时间,所以没有必要去测量它;
基本功能:自行打印生命周期的时间值进行比较
打开方式:通常抓取系统时钟时间或者根据日志本身输出的时间进行比较
说明:
1,灵活自定义,但依赖于系统暴露的生命周期方法,比较适用于抓application启动到首个界面显示的时间;
2,注意适用Android的Log类进行打印,尽可能排除打印逻辑的耗时。
示例:
对于冷启动,我们比较关注application启动到首页显示的时间,比较适用的手段就是按需打印日志,此时可供选择的生命周期回调方法如下:
-> Application 构造函数
-> Application.attachBaseContext()
-> Application.onCreate()
-> Activity 构造函数
-> Activity.setTheme()
-> Activity.onCreate()
-> Activity.onStart
-> Activity.onResume
-> Activity.onAttachedToWindow
-> Activity.onWindowFocusChanged
如果记录冷启动启动时间一般可以在 Application.attachBaseContext() 开始的位置记录起始时间点,可以在第一次回调 Activity.onWindowFocusChanged 记录应用启动的结束时间点。
基本功能:通过shell命令启动一个Activity,抓取启动的三个参考时间
打开方式:adb shell am start -W -n [package]/[activity with full path]
说明:
1,灵活自定义,但依赖最好根据APP本身的分页逻辑以此打开,避免非正常的初始化逻辑,但同时也可以检测非正常逻辑下APP的稳定性。
2, 三个值中,可重点关注TotalTime-当前Activity启动的时间,不包括等待上一个界面onPause,onStop的时间。
备注:onDestroy在下级界面onResume之后才回调,所以不会影响子界面的启动,但是很明显会影响之后的任务,因为onDestroy也是在UI线程。
示例:
adb shell am start -W -ncom.example.tfguo.myapplication/com.example.tfguo.myapplication.MainActivity
-》输出:
Starting: Intent {cmp=com.example.tfguo.myapplication/.MainActivity }
Status: ok
Activity:com.example.tfguo.myapplication/.MainActivity
ThisTime: 1133
TotalTime: 1133
WaitTime: 1154
-》对比ActivityMananger日志
I/ActivityManager: Displayedcom.example.tfguo.myapplication/.MainActivity: +1s164ms
adb shell am start -W -ncom.example.tfguo.myapplication/com.example.tfguo.myapplication.MainActivity
-》输出:
Status: ok
Activity:com.example.tfguo.myapplication/.MainActivity
ThisTime: 310
TotalTime: 310
WaitTime: 318
-》对比ActivityMananger日志
I/ActivityManager: Displayedcom.example.tfguo.myapplication/.MainActivity: +320ms
可见,ActivityManager日志得到的值要略大一些。
基本功能:检测主线程的耗时问题
打开方式:系统设置-》开发者模式-》打开严格模式,主线发生程耗时事件时屏幕呈现红色闪烁。
说明:简单直观的发现主线程的耗时问题,如果需要进一步定位耗时方法,则可以使用method跟踪工具进行分析;如果发现layout显示的耗时,还可参考第一篇布局优化。
备注:也适用于APP的其它运行场景,但由于启动时主线程任务需要重点留给UI,因此需要在此时重点关注。
示例:略
基本功能:检测各个线程的方法的耗时情况
打开方式:Studio-》Monitor-》CPU-》Start Method Tracing
说明:
1,定位耗时的主流工具包括DDMS的MethodProfiling/Traceview,Studio的Method Tracing
2,这里根据易用性和适用场景,介绍MethodTracing
3,方法跟踪工具使用于所有需要定位方法耗时相应的场景
示例:
1, 点击开始和结束
2, 文件保存目录
3, 可以按线程查看
4, 可以搜索专门的方法
5, 查看各个方法的耗时
基本功能:检测主线程的耗时任务
打开方式:引入StrictMode类,设置相关方法
说明:Android2.3后引入,通过日志、dialog等方式提醒耗时任务,主要针对网络、IO、数据库耗时,也可以自定义一些耗时监控策略。
示例:
在application初始化时设置
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // detectAll() for all detectable problems
.penaltyDialog() //弹出违规提示对话框
.penaltyLog() //在Logcat 中打印违规异常信息
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog() // .penaltyDeath()
.build());
}
这里指用户交互的响应,影响包括滚屏、点击、弹框、动画等的交互体验,可以理解为主线程的操作响应。
基本思路:
1, 理清响应任务,减少和避免非UI逻辑抢占主线程
2, 优化任务时序,做好线程、进程间的通信和调度
基本要求:
1, 慢速滚屏正常无卡顿,UI显示无明显延迟
2, 快速滚屏无明显卡杜,每个item的getView控制在16ms以内
备注:
快速滚屏即使已经优化合格,但内存方面可能会因为GC的关系,导致全线程暂停而有卡顿,减少GC参考内存优化篇。
根据模块和业务的不同,主流面采用如下:
1, 上下和左右滚屏
2, 点击、切换等其它会提交到后台的交互
1, 列表加载任务过重,滚屏响应较慢,甚至丢失部分交互
2, 耗时任务未起子线程,卡屏甚至ANR
基本功能:检测各个线程的方法的耗时情况,包括手动方式检测和代码方式检测
打开方式:
1, DDMS-》Method Profiling
2,代码引入Debug.startMethodTracing和Debug.stopMethodTracing方法
说明:
手动方式较为简单易用,也较为粗放;而该工具对比Studio Method Tracing的优势就在于可以代码控制具体跟踪点,在此示例着重介绍代码方式。
示例:
1, 分析代码,选择需要定位跟踪的方法,在开始和结尾打上trace
Debug.startMethodTracing("traceFile");
func();
Debug.stopMethodTracing();
运行程序, 会在sdcard上生成一个"GithubApp.trace"的文件,检查增加权限
2,通过adb pull将其导出到本地
adb pull/sdcard/traceFile.trace
3,打开DDMS,open导出的文件
4,也可手动方式获取文件
基本功能:检测实时网络访问情况
打开方式:Studio-》Monitor-》Network
说明:
适用于观察网络请求时机是否合理,识别上行请求量设置是否合理,网络图片是否缓存
功能为DDMS的Networkstatics的简化版
示例:
基本功能:内存、CPU、GPU、电量以及日志插桩sdk集成
示例:gt.qq.com
基本功能:方法注解打印,包括参数值,方法耗时
示例:https://github.com/JakeWharton/hugo
基本功能:
在Chrome DeveloperTools查看App的布局,网络请求,Sqlite,preference,一切都是可视化的操作,无须自己在去使用adb,也不需要root你的设备。使用的方式很简单,配置好之后,在Chrome地址栏输入chrome://inspect。
说明:
网络请求的话,需要使用下面的两个库之一(okhttp或者urlconnection)。
示例:https://github.com/facebook/stetho