|
android使用工具性能优化 简介 
本文记录使用工具来对app进行优化过程,主要包括UI界面优化、内存优化、代码优化以及电量优化;各个优化模块是相互关联的,各个模块优化后才能达到app整体的性能提升。 UI界面优化 界面优化方面主要是减少GPU过渡绘制(也就是同一个像素点多次绘制)以及优化渲染时间,优化点主要是:
- 减少布局层次空间
- 视图View的绘制draw方法减少耗时操作
如何才能完成上述的布局优化,本篇不做阐述,可以查看我的GPU调试笔记
代码优化 代码优化范围就很广,涉及的知识也很多!首先,在编码时,我们要打造一个 适合具体项目代码框架,复用相同的代码,打造高内聚、低耦合的代码;不同业务场景使用不同的数据结构,权衡时间和空间效率,复杂耗时操作要活用线程或者进程;然后,当我们编写好代码后,我们反向来检查优化代码时,可以使用一些工具来优化代码 monkey压力测试 monkey是adb中的一个测试脚本工具,当我们输入命令后,monkey可以模拟点击、滑动、长按等操作,随机去操控我们的App,以检测App中意料之外的bug,最后输出一份测试日志文件供我们去查看出现的bug;具体的monkey测试步骤如下:
操作指令:
adb shell monkey -p packagename -s 20 --throttle 1500 --ignore -crashes --ignore -timeouts -vvv 1000 |
-p: 测试的包名
-s: 种子数,可以理解为各种触摸点击次数顺序是通过一个算法实现的,而这个算法需要输入一个时间因子,相同的时间因子,产生的触摸事件是一模一样的
–ignore-crashes --ignore-timeouts: 忽略测试过程中产生异常和超时,否则测试会终止
-v:表示日志等级,默认是v,vvv是verbose最详细等级
1000:表示事件的次数
–throttle:事件操作后的延时
为了保证测试覆盖App所有页面,可以写个脚本,多执行几次,变换不同的时间因子;最后去日志文件查询崩溃日志
此项功能要打开USB模拟点击功能,不然无法调试 UI优化总结- 布局不要嵌套太多层
- 使用merge减少布局
- 自定义View onDraw不要放置复杂操作
- 合理使用validate和requestLayout
TraceView工具优化代码 当我们调试代码时,如遇到某段业务逻辑执行速率慢时,无法定位出哪块代码导致时,可以使用traceView工具进行定位;traceView工具可以展示代码执行的耗时、次数
该工具位于android sdk的tools/monitor目录下
先来看一下traceView工具界面

如上图:
区域A:展示的是多个线程以及线程号
区域B:A区域每个线程执行函数的时间分布图,线程中每个函数标记为一种唯一的颜色,颜色的长度标明这个函数执行时间的长度;所以从这个分布图中可以很明确的找出谁执行得慢
区域C:和区域B相关,每个函数的执行速率结果,还有函数执行顺序,当前函数由谁调起,它又调用了哪些其他函数;重点阐述横向的参数:
incl cpu time:函数获取CPU时间片执行时间,包含函数内部调用其他函数时间;
excl cpu time:函数自身CPU执行时间,不包含调用的下级函数时间
incl real time:函数执行时间,包括函数内调用其他函数时间,也包含被线程中断休眠阻塞的时间
excl real time:同上,但是不包括调用其他函数时间
Calls+RecurCalls/Total:程序调用次数+递归调用次数/总次数
Cpu Time/Call:函数执行获取时间片平均时间
Real Time/Call:函数执行时间包括休眠时间,平均时间 如何快速找出效率低函数? 两个关键点:(1) 函数本身比较耗时;(2) 函数调用次数过多
首先,可以很直观的在上图B区域,快速找出颜色最长的也就是函数耗时最长的;其次,也可以在C区域点击CPU TIme/Call,C区域会自动排序长度;同理点击Calls+RecurCalls/Total自动排序调用次数;但是C区域函数比较多,我们可以在C区域下方有个Find进行输入过滤,能快速找到低效率函数,然后我们去查看逻辑进行优化即可 优化demo 下面,我将使用上述方法来优化一段打开相机扫一扫的逻辑,进行优化:
- 首先从主观感受上,我打开相机感觉比较慢,我需要定位trace代码的启动和终点
- 起点我使用Debug.startMethodTracing(“xxx.trace”)进行开启trace起点,假设扫一扫页面为ScanActivity;那我这个起点在打开这个页面的前一句代码
- 终点我设置在ScanActivity的onWindowFocusChanged方法,该方法表明activity真正可见,不要设置到onResume;使用Debug.stopMethodTracing();停止trace
- 最后运行app完成后,会在/sdcard/Android/data/packageName/files/xxx.trace路径下,adb pull出来
- 使用monitor打开这个文件;以下是我对trace文件的分析:
注意,定位trace代码块除了上面的Debug插入代码这种方法外;还可以直接使用studio的Profile功能,在CPU性能图下面,直接点击trace start和stop
优化点1,相机参数getParameters

通过CPU Time/Calls排序后,加上书图com过滤后发现:
早SurfaceCreated函数内部耗时的原因是initCamera和isFlashing这两个函数;进入这两个子函数查看:


如上两图,在isFlashlightAvailable和initCamera两个方法内主要的耗时都在Camera.getParameters这个函数,进一步查看函数:
public Parameters getParameters ( ) {
Parameters p = new Parameters ( ) ;
String s = native_getParameters ( ) ;
//耗时主要在下面这个方法
p . unflatten (s ) ;
return p ;
} |
p.unflatten会遍历相机的所有参数并封装构造到一个Map结构;这也是它耗时的原因,并且这个函数是库函数无法修改;但是我们程序多个函数都回去掉这个函数,处理的办法就是缓存获取的相机参数,并且为了保持参数的有效性,加入了一个过期时间;修改如下:
private Camera .Parameters getCamerPara ( ) {
if (cachParams == null || System . currentTimeMillis ( ) - lastParaTime > 60000 ) {
cachParams = mCamera . getParameters ( ) ;
lastParaTime = System . currentTimeMillis ( ) ;
}
return cachParams ;
} |
把之前的用到Camera.getParameters()的地方全部改为这个函数即可;优化后重新trace结果如下图:

优化点2,setContentView布局
从traceView文件中发现setContentView也比较耗时:

setContentView时间的长短取决于布局文件xml的层叠嵌套,查看布局发现,根部据里面嵌套了一层ToolBar,在ToolBar里面有我们的左右标题TextView;我修改为去掉ToolBar布局,把左右布局提出来在根布局下面,这样整个布局文件就只有一层布局了
最后,优化后trace的结果,setContentView耗时减少:

优化总结- 复杂耗时逻辑放入子线程、进程或者服务端
- 一些网络请求使用缓存
- 大量数据分批操作,如在屏幕内时才加载 屏幕外不加载
- 使用Service后台进行
内存优化Profile 移动端设备内存有限,需要合理使用内存资源,及时释放不需要的内存,减少内存泄漏;这里用到的工具是Android Studio的Profile,点击Run下面的Profile功能,进入MEMORY内存下面,大致如下图所示:

简单介绍下上述界面各个区域:
C区域:内存时间线,内存是实时的,可以查看过去到现在任意时刻的内存状况,内存增加时,图形会上升,内存释放或者垃圾回收时,图形会下降,如C区域右下角的垃圾桶符号就是一个垃圾回收标志。
B区域:垃圾桶标志,点击后就会主动进行垃圾回收;下载符号,是对app内存进行dump标志,后面的allocation Tracking菜单选项,是内存app dump选项,有sample和full选项,sample方式dump内存是对app影响较小,而full较大;dump内存后,内存明细就时上图下班部分的样子
A区域:实时展示app内各个模块内存大小;
- java 从java或kotlin代码分配的对象内存
- native 从C/C++代码分配的内存
- Graphics 从图像缓冲队列输出到屏幕的像素(如GL表面/GL纹理)所占用的内存
- Stack 应用中原生堆栈和java堆栈所用的内存
- Code 代码资源(dex字节码/dex优化字节码/so库和字体等)
- others 应用占用的内存系统无法归类
- Allocated java和kotlin代码的对象个数
D区域:dump的内存实例详细展示,左上方方框内是查看方式选择;
- default heap:当系统未指定堆时。
- image heap:系统启动映像,包含启动期间预加载的类。此处的分配保证绝不会移动或消失。
- zygote heap:写时复制堆,其中的应用进程是从 Android 系统中派生的。
- app heap:您的应用在其中分配内存的主堆。
- JNI heap:显示 Java 原生接口 (JNI) 引用被分配和释放到什么位置的堆。
右侧的菜单中,选择如何安排分配:
- Arrange by class:根据类名称对所有分配进行分组。这是默认选项。
- Arrange by package:根据软件包名称对所有分配进行分组。
- Arrange by callstack:将所有分配分组到其对应的调用堆栈。
D区域右侧还有许多菜单:
Allocations:java内存实例数量
native size:此java对象使用的native层的内存量,字节单位
shallow size:此java对象使用java内存总量
Retained Size:为此类的所有实例而保留的内存总大小
当我们选择D区域某个对象实例时,会在右侧展示EF区域
E区域:为D区域该实例拥有的所有成员变量
- depth 对象实例到GC根节点的跳数
- Native Size:java对象所有所拥有native层的占用的内存
- shallow size和Retained Size同上
F区域:对象实例被别的对象持有的引用
memory的功能介绍完了,那这个工具是如何进行内存优化呢?
首先,需要给app一些压力,极限测试下,实时查看内存分配请看,有没有内存激增的异常状况;其次,主动垃圾回收后,dump内存下来,查看内存实例,主要看内存泄漏,一些该释放而没有被释放的实例。这里我们也可以配合monkey测试框架自动完成内存优化
profile memory例子 我所采用的调试方法:
我先使用monkey自动测试,随机操作app,最后然其停留在主页面HomePage,手动点击垃圾回收,dump内存查看检查,和主页面不相关的实例都不应该存在,需要检查实例的对象数量,占用内存是否太大不合理等;app的其他页面也类似,都可以按照此操作进行,以下时我dump内存发现的异常问题:
实例数量过多 
ServiceCreator这个类时我底层网络框架的基础类,网络访问接口都是由这个类生成的;我代码设计的本意ServiceCreator时唯一的,不会存在多个实例,那这里时不对的,查看源码如下:
//网络底层服务
@Binds
abstract IServiceCreator serviceCreator (ServiceCreator serviceCreator ) ; |
我使用的时dagger2框架,这里采用的时@Binds接口和实例绑定,而问题出在没有给他设置单例唯一,解决的办法加个注解@Singleton解决问题;不清楚dagger2的可以参考这篇文章
下面截图也是实例过多,同一个实例高达6个

查看这段源码:
window . findViewById (R .id .tv_mv_phone ) . setOnClickListener ( this : :onClickViews ) ;
window . findViewById (R .id .tv_check_user ) . setOnClickListener ( this : :onClickViews ) ;
window . findViewById (R .id .tv_check_private ) . setOnClickListener ( this : :onClickViews ) ;
window . findViewById (R .id .about_us ) . setOnClickListener ( this : :onClickViews ) ;
window . findViewById (R .id .btn_logout ) . setOnClickListener ( this : :onClickViews ) ;
userIcon = window . findViewById (R .id .btn_login ) ;
userIcon . setOnClickListener ( this : :onClickViews ) ; |
上面使用lambda表达式设置组件view监听,熟悉lambda表达式原理的人都知道这时怎么回事,不熟悉的点击这里,解决的办法就是不用lambda,直接设置为一个监听 Profile里面的Network和Energy调试 这两块主要网络模块和能耗模块,查看网络流以及一些服务能耗。
关于Network模块,查看一些网络访问数据和频率,减少不必要的发送数据,以及减小发送频率;
Energy模块,主要是一些服务耗电,如定位等,不使用定位时,关闭定位服务。
以上就是性能调试的记录!如有不正或更好的办法,望不吝赐教!
----------------------------
原文链接:https://blog.csdn.net/jackzhouyu/article/details/104219693
程序猿的技术大观园:www.javathinker.net
[这个贴子最后由 flybird 在 2020-03-09 22:11:29 重新编辑]
|
|