作者:张力尹
先来点废话:
听说最近 Android 岗位变多了,你去面试了么?
面试官:你简历中提到了卡顿优化,做了哪些优化呢,展开说说。
你:哦,脑子飞速闪过网上的文章,然后说内存泄漏、内存抖动、启动优化、布局优化、图片优化、网络优化…
面试官:细节呢,详细些。
你:哦,啊,哈哈哈…\
文章初衷:
网上优化文章很多,各种主题都有,每次看那些大牛文章都觉得好有道理,啊我会了,可实际应用到项目的时候又不知道从哪里下手…
咋的,说的是不是你?反正这真的就是我。所以想着把自己做的一些小优化分享出来,主题尽量明确,看后更易上手,让世界更美好「为以后的面试润色」…
先不引入 Matrix 等第三方库,本文宗旨:先从简单处入手,尽量主题明确
。
Android Profile
子曰:“工欲善其事,必先利其器。居是邦也,事其大夫之贤者,友其士之仁者。”
如果你刚刚开始优化,那就别上来就扯 systrace 和 traceview「已被废弃」 了,先看看 Android Profile 吧。看看它能做什么:
- CPU 性能分析器有助于查出运行时性能问题。
- 内存分析器有助于跟踪内存分配情况。
- 网络性能分析器可监控网络流量使用情况。
- 能耗性能分析器可跟踪能耗情况,这有助于分析电池电量消耗过快的问题。
内存泄漏
ps:如果熟练此段可略过。另外只涉及到 java 层,未涉及到 native 层泄漏。
几年前,提到内存泄漏,很多人都会想到 LeakCanary,上手简单,而且去面试的时候很多时候还会问 LeakCanary 实现原理 / 为什么不能在线上应用等等…
现在时代换了,官方直系血亲来了。
本文使用的Studio版本:Android Studio Giraffe | 2022.3.1 Beta 1
先说定位步骤
-
创造泄漏环境:
App 多点几个页面,复杂页面可执行多次进入/退出的操作,再回到App 首页; -
打开Profile 界面:
- 点击如下绿框选中icon
- 呈现如下图,内存问题排查需要进入到 memory 模块
- 捕获堆转储「即选择绿框内容」,并点击record,会自动开始分析,等待即可
- 自动停止后,会出现如下展示:
- 选择绿框选项,会筛选出 activity/fragment 层级的内存泄漏
卡顿「掉帧/ANR」
适用场景:已知某个行为会触发卡顿,eg:点击到某个tab,进入某个页面
先说定位步骤
- 进入Profile,点击进入 CPU 模块
- 选中第四个「Java/Kotlin Method Sample」,点击record
- 自己点击stop后,等待生成文件,有需要可以右键保存为 trace 文件;双击 main 模块,点击具体分析 UI 线程做了啥大事。
看到上图其实已经大概意识到事情没这么简单了…为啥?看颜色:
- 对系统 API 的调用显示为橙色;
- 对应用自有方法的调用显示为绿色;
- 对第三方 API(包括 Java 语言 API)的调用显示为蓝色
tip:调节下图绿框中的两个箭头位置也可以放大/缩小
接下来就是重点看绿色部分,看看自有方法为啥导致绿色长度这么明显。
解决的问题
下面列出来的,都是我通过上面的方式,解决的部分问题。
声明:以下数据均比生产环境的值要小…
1.PackageInfo 相关的问题
通过 CPU Profiler 可以看到下图
/**
* App 通过 PackageManager 去获取应用信息,是卡顿操作,若多个地方调用应存储为常量
*/
val packageInfo: PackageInfo? by lazy { PackageUtils.getPackageInformation(App.context) }
val applicationInfo: ApplicationInfo? by lazy { packageInfo?.applicationInfo }
val packageName by lazy { packageInfo?.packageName }
val packageVersionCode by lazy { packageInfo?.versionCode }
val packageVersionName by lazy { packageInfo?.versionName }
val firstInstallTime by lazy { packageInfo?.firstInstallTime ?: 0L }
val lastUpdateTime by lazy { packageInfo?.lastUpdateTime ?: -1L }
2.Websocket 使用问题
如下图,发现 UI 线程里面有大量 GSON 解析的操作,再跟踪,发现 App 中对于 WebSocket 的数据接收和处理,基本上是 UI 线程,造成掉帧 or 卡顿 or ANR。其实也惭愧,这个问题竟然之前没注意…
对于我们的 App 来讲,更致命,因为场景主要是歌房,消息基本都是通过 ws 来处理的,里面有各种UI的频繁更新、各种动效、各种动画、歌曲演唱…
所以凭借这个优化了整个App 对于 ws 的使用,改完后经住了测试组的压测、发热/卡顿等都好了很多。
3.EmojiManager.renderEmoji 方法
4.图文混排
项目中应该都有这种形式,text+icon 这种,如果在 RecyclerView 中使用,那卡顿就比较明显了
5.骨架屏耗时
这是之前的业务需求,后来一跑太耗时,直接下掉了…
6.获取通知是否可用
业务中很多关于是否获取通知权限的判断,这种case 应该存为全局常量,必要情况再去重新获取值。
Profile 部分小结
这里说的只是初步使用,还有更细节且更精准定位某个方法的方式,需要去继续探索了。不过虽是初步使用,但是在我公司的项目里解决了不少问题,大家也开始吧 ~ 在一些场景下用 Profile 去定位卡顿是不方便的,所以有很多优秀的框架 Matrix、Booster、KOOM 等。
线程优化
这个主题很大,开始的话,时间也比较长,前期储备较多知识点,具体应用还要结合自己的项目,我在这里只是提一下主题。
最后说点
- 上面关于 LeakCanary 的问题,不会的话去查一查吧,再深入的话会接触到 Matrix ,关于 Matrix 的记录我也写过,看这里Matrix-TraceCanary 实际使用* 本文重点是在整理,提到的东西不新,但是比较经典。
- 整理这个过程挺好的,过程中会锻炼自己的总结概括能力,仔细审视自己提及到的知识点是否正确,所以你也开始吧。
知识体系
非常重要,零散接受知识点的话比较被动,所以学习过程中就需要整理自己的知识体系,这样面试的时候就可以从点到面展开了「我假设的」。- 多看 Android 官方文档,看 Android 官方文档提到的项目,比如你想
系统学习
最近很火的 MVI/Compose。 - 身处旋涡,就再挣扎一下。
- 其实这些乱七八糟的写的不是我本意,但是逼着自己写一下。为什么我会这么费尽心思的想写点啥? 因为掘金姐告诉我了,只要我好好写,就给我一个证书,然后我就想用那个证书发个朋友圈… 你会帮我点赞的对么?
为了帮助到大家更好的全面清晰的掌握好性能优化,准备了相关的学习路线以及核心笔记(还该底层逻辑):https://qr18.cn/FVlo89
大家可以进行参考学习:
性能优化核心笔记:https://qr18.cn/FVlo89
启动优化
内存优化
UI优化
网络优化
Bitmap优化与图片压缩优化:https://qr18.cn/FVlo89
多线程并发优化与数据传输效率优化
体积包优化
《Android 性能监控框架》:https://qr18.cn/FVlo89
《Android Framework学习手册》:https://qr18.cn/AQpN4J
- 开机Init 进程
- 开机启动 Zygote 进程
- 开机启动 SystemServer 进程
- Binder 驱动
- AMS 的启动过程
- PMS 的启动过程
- Launcher 的启动过程
- Android 四大组件
- Android 系统服务 - Input 事件的分发过程
- Android 底层渲染 - 屏幕刷新机制源码分析
- Android 源码分析实战