NimbleDroid 是美国哥伦比亚大学的博士创业团队研发出来的分析Android app性能指标的系统,分析的方式有静态和动态两种方式,
流畅度优化
显示原理
- cpu计算: measure layout draw => displayList
- gpu栅格化: displayList => 位图
- 屏幕显示: 位图 => 显示 60hz = 1000/60 = 16.67ms
检测流畅度的几种方式
- 腾讯GT https://github.com/Tencent/GT
- BlockCanary https://github.com/seiginonakama/BlockCanaryEx
- 开启GPU渲染模式 水平的绿色横线是 16ms android 6.0以上颜色多种
image.png
- Swap Buffers:表示处理任务的时间,也可以说是CPU等待GPU完成任务的时间,线条越高,表示GPU做的事情越多;
- Command Issue:表示执行任务的时间,这部分主要是Android进行2D渲染显示列表的时间,为了将内容绘制到屏幕上,Android需要使用Open GL ES的API接口来绘制显示列表,红色线条越高表示需要绘制的视图更多;
- Sync & Upload:表示的是准备当前界面上有待绘制的图片所耗费的时间,为了减少该段区域的执行时间,我们可以减少屏幕上的图片数量或者是缩小图片的大小;
- Draw:表示测量和绘制视图列表所需要的时间,蓝色线条越高表示每一帧需要更新很多视图,或者View的onDraw方法中做了耗时操作;
- Measure/Layout:表示布局的onMeasure与onLayout所花费的时间,一旦时间过长,就需要仔细检查自己的布局是不是存在严重的性能问题;
- Animation:表示计算执行动画所需要花费的时间,包含的动画有ObjectAnimator,ViewPropertyAnimator,Transition等等。一旦这里的执行时间过长,就需要检查是不是使用了非官方的动画工具或者是检查动画执行的过程中是不是触发了读写操作等等;
- Input Handling:表示系统处理输入事件所耗费的时间,粗略等于对事件处理方法所执行的时间。一旦执行时间过长,意味着在处理用户的输入事件的地方执行了复杂的操作;
- Misc Time/Vsync Delay:表示在主线程执行了太多的任务,导致UI渲染跟不上vSync的信号而出现掉帧的情况; 是重点优化对象 配合cpu profile 看看耗时任务
-
StrictMode 可检测耗时
-
过度绘制问题:一个像素点被绘制多次
image.png -
androidStudio 的布局分析器 查看布局结构
第三方布局分析器 https://github.com/romainguy/ViewServer
布局嵌套太深导致 measure 计算太多 尽量减少布局嵌套 少用 wrap_content
流畅度优化手段:
- 避免过度重绘,移除不必要的背景,降低布局嵌套层数 和 wrap_content 减少measure次数
可使用 约束布局 减少 组合嵌套 线性布局和相对布局, 可使用 include merge等等 还可以用viewStub延迟初始化一部分view 第三方工具x2c 把xml布局 转换为代码实现, 减少读取布局文件占用的时间- 避免主线程耗时 (gpu模式 深绿色),比如与视图有关的 model 要在io线程处理好 再在主线程渲染
- 大杀招:用IdleHandler 进行预处理
- 避免频繁gc 一般来说瞬间大量产生对象一般是因为我们在代码的循环中new对象, 或是在onDraw中创建对象等. 所以说这些地方是我们尤其需要注意的...
- 刷新的时候局部刷新
网络优化
1.抓包工具Wireshark, Fiddler, Charles
2.解决方案
- 减少Radio(频射模块)活跃时间
- 也就是减少网络数据获取的频次.
- 这就减少了radio的电量消耗, 控制电量使用.
- 减少获取数据包的大小
- 可以减少流量消耗
- 也可以让每次请求更快, 在网络情况不好的情况下也有良好表现, 提升用户体验.
图片上传与展示 实战
a.api网关,避免一个页面的数据来自多个接口
b.压缩
c.网络缓存
d.弱网优化
e.不适用域名 使用ip 绕过dns解析步骤
图片上传 和 图片查看器
- 采用鲁班压缩 和gzip 对上传的图片和数据进行压缩
- 显示时采用缩略图策略,魔图规则
- 减少不必要的网络请求,而采用客户端的基本逻辑去改变页面 例如点赞按钮后 直接本地+1
- 打包网络请求
- 动态超时时间
- wifi下与拉取网络数据 例如网易的离线新闻 或者 离线更新包
- 本地缓存
- http缓存
- 控制网络请求频率
- 网络请求 接口降级
- 重试机制 重试次数
- 弱网 无图模式
- ip直连 避免dns 或者使用 httpdns
- webp图片 能省1/3
- http2 二进制多路复用
- 避免轮询 采用长连接
电量优化
检测工具 energy profile
用adb命令 Batterystats 导出 电量记录文件
然后用 谷歌开源的 batteryHistorian 导入记录文件 分析电量情况
- 和网络优化息息相关,因为网络非常耗电量,做好网络优化 手机的通过内置的射频模块和基站 而这个射频模块(radio)是非常耗电的.
- 及时释放wakeLock (保持屏幕常亮)
- 合理释放GPS的监听
- BatteryManager 监听充电状态,一些耗电操作可以在充电情况去做
- 定时任务 可以做暂停 例如 轮播图 不可见时 要及时停止
启动优化
冷启动:直接启动
热启动:退到后台后进程没死,activity没死
温启动:退到后台进程没死,但activity被回收
检测工具 GT
app启动优化
- 将application oncreate中的方法 拆分成 同步和异步, 并且延迟初始化一部分 加入一个
- windowBackground 作为启动页背景 首屏尽量简单 可以写一个什么都不做的activity 只顾跳转, 不做setcontentview
- 善用分析工具
- 优化包体积可以加速启动,优化dex分包可以加速启动
- 这里可以说下方舟编译器的原理
安卓系统中也有这样的编译器,目前有两个编译器,分别是Art和Jit,Art是在你首次安装APP时,对大约20%的核心程序代码翻译成0、1这样的机器语言保存在手机中,另外的80%非核心代码则在运行时用Jit编译器来翻译,所以才有了华为所说的连执行连翻译,自然效率不高了。
而如果APP通过方舟编译器开发,打包之后的APP就直接以0、1这样的机器码存在,这样安装到手机之中后,就是0、1这样的机器码,不存在翻译过程,机器就直接可以执行,不需要转换,自然效率更高,体现在操作上自然就是启动更快,操作更流畅了
https://baijiahao.baidu.com/s?id=1631785697366805454&wfr=spider&for=pc
- 把一些方法抽取出来. 在页面绘制后, 等到UI线程空闲的时候, 再去执行这些耗时方法.idlehandler
包体积优化
- 图片资源压缩 tinypng ,也可使用webp 有损压缩, 支持透明通道
- 尽量用 xml shape 替代切图,或者使用.9png 替代大图
- release去除多余so,保留 arm-v7
- 只保留一xxhdpi的切图 可使用字节跳动的方案 解决适配问题
原理 px = dp * density 去根据宽度修改 density,保证 写死dp的和宽度的比例 去适配不同的宽度 - 方舟编译器
- 混淆
- 去除无用资源 ,去除用的语言支持
defaultConfig {
resConfigs "zh"
}
buildTypes {
release {
shrinkResources true
}
}
- 大杀器 插件化
- gradle provided ,根据不同渠道,可能有不同的功能, 使用provider,会让你通过编译,但不会加入到apk
- redex 是 Facebook 开源的一款字节码优化工具
- 减少gc次数
内存优化
检测工具:leakCanary mat
避免频繁gc 每次gcandroid系统可能会冻结200ms
避免内存占用过多
- 使用 高效的数据结构 SparseArray 不是线程安全的
sparseArray<E> 替代 hashmap<Integer,E>
sparseBooleanArray 替代 hashmap<Integer,Boolean
sparseIntArray替代 hashmap<Integer,Integer>- 大图加载 使用 瓦片的形式 加载 显示的部分
- Lru 过期内存中的对象,防止长期占用
- 内存告警的时候及时释放对象,(图片)
- 避免使用线程池自带的四种构造方式 可能oom
避免内存泄漏
- context被静态变量持有
- context被单例持有
- context被属性动画持有, ondestory 要cancel动画
- context1被内部类持有 被handler持有, 要使用静态内部类
- context被application持有
- 资源没关闭造成内存泄露 bitmap
总结
1、对于生命周期比Activity长的对象如果需要应该使用ApplicationContext,在需要使用Context参数的时候先考虑Application.Context.
2、在引用组件Activity,Fragment时,优先考虑使用弱引用。
3、在使用异步操作时注意Activity销毁时,需要清空任务列表,如果有使用集合,将集合清空并置空,释放相应的资源。
4、内部类持有外部类的引用尽量修改成静态内部类中使用弱引用持有外部类的引用。
5、 留意活动的生命周期,在使用单例,静态对象,全局性集合的时候应该特别注意置空。
网友评论