整体思路:存量App:先观测定位,再分模块治理,最后建立监控防回退;新建App:架构、编码、编译、基线监控提前预埋,从源头避免性能坑
性能维度覆盖:启动、UI流畅度、内存、包体积、网络、耗电、IO,下面分开讲。
一、拿到一个已有应用,性能优化完整流程
阶段1:摸底 + 建立基线(最重要,不要上来直接改代码)
目标:拿到客观指标,区分「必改严重问题」和「优化锦上添花」,避免盲调。
- 确定性能指标基线
| 指标 | 工具 | 采集内容 |
|---|---|---|
| 启动耗时 | Android Studio Profiler / ADB 冷启动命令 | 冷启动、温启动、热启动;adb shell am start -W 包名/Activity
|
| 帧率、卡顿 | Perfetto / Choreographer 埋点 / BlockCanary | 主线程卡顿、掉帧、ANR、长耗时方法 |
| 内存 | Profiler、LeakCanary、MAT | 内存抖动、内存泄漏、峰值内存、OOM场景 |
| 包体积 | Android Studio Analyzer | dex、资源、so、assets占比 |
| 耗电 | Battery Historian | WakeLock、后台唤醒、频繁传感器、网络 |
| IO | Perfetto | 主线程磁盘读写、数据库慢查询 |
| 网络 | Charles/OkHttpInterceptor | 重复请求、大报文、串行请求 |
-
梳理问题清单,分级
- P0(阻塞上线,优先修复):ANR、OOM、频繁崩溃、冷启动巨慢、页面严重卡顿
- P1(体验明显变差):内存持续上涨、页面间歇性掉帧、后台耗电高、包体积过大
- P2(体验微调):轻微内存抖动、非核心页面小幅掉帧
-
梳理App架构现状
- 页面栈、组件:Activity/Fragment/Compose、多进程?
- 网络库、图片库、数据库、路由、埋点、第三方SDK清单(很多性能问题来自第三方SDK)
- 图片加载、对象创建、主线程任务、资源加载逻辑
阶段2:分模块定位+修复(按收益优先级)
1)启动优化(收益最高)
- 区分冷/温/热启动,Application 是重灾区
- 禁止在
onCreate主线程做重任务:初始化SDK、数据库、解析大配置、加载大图 - 懒加载:非核心SDK延迟初始化,放到首屏渲染完成后(
IdleHandler) - 多进程:区分主进程和其他进程初始化,避免所有进程全量初始化
- 避免静态成员大量初始化,静态对象持有大资源
- 禁止在
- Activity/Fragment:
onCreate/onStart减少耗时,减少inflate嵌套,延迟非必要View创建 - 编译:R8/Proguard开启,资源压缩,dex分包
2)UI & 卡顿优化
核心原则:主线程只做UI相关,任何耗时都丢到子线程
- 传统XML:减少布局层级,避免过度绘制;使用
ViewStub懒加载;减少invalidate频繁调用;避免在onDraw创建对象(内存抖动) - Compose:
- 合理使用
remember、derivedStateOf,避免不必要重组 - 不要把宽高、状态直接在顶层传入,尽量缩小重组范围;稳定key(
key{})处理列表 - 避免Modifier顺序踩坑(background/padding/clickable顺序影响绘制和点击区域)
- 合理使用
- 卡顿监控:接入BlockCanary,捕获主线程阻塞堆栈;Perfetto抓取完整trace定位长耗时函数
3)内存优化
- 泄漏:LeakCanary自动捕获,重点排查:静态引用持有Activity、匿名内部类、Handler消息、异步回调、注册监听未反注册
- 内存抖动:循环内创建对象(字符串、集合、Bitmap),
onDraw新建对象;用对象池、复用数组 - Bitmap:复用内存池,合理inSampleSize,避免大图一次性加载;Compose图片使用
rememberAsyncImagePainter控制尺寸 - 集合:及时释放大集合,避免长生命周期对象持有大图/大列表数据
4)包体积优化
- 资源:webp压缩图片,删除无用资源
shrinkResources;资源混淆;只保留对应ABI的so - Dex:R8开启代码压缩混淆,移除无用代码;动态功能模块(Dynamic Feature Module)拆分非核心业务
- 清理冗余SDK,替换重SDK为轻量版本
5)网络 & IO & 耗电
- 网络:请求合并、预取/懒加载、缓存策略;避免短时间大量请求;复用OkHttp连接池
- IO:数据库用事务,主线程禁止读写SP/文件;SP不要存大数据;推荐DataStore替代SP
- 耗电:控制WakeLock;减少频繁GPS、蓝牙;批量上报埋点,不要高频单点上报
阶段3:验证 + 建立持续防护(防止优化后回退)
- 回归复测:对比优化前后基线指标,确认指标改善,不能只靠肉眼感受
- 接入线上监控:
- 性能埋点:冷启动耗时、页面FPS、ANR、OOM、内存峰值
- 性能告警:指标超过阈值告警(例如冷启动>2s触发告警)
- 提交代码增加CR性能检查项,定期回归性能基线
存量App优化一句话总结:先抓数据定基线 → 优先解决P0问题 → 逐个模块治理 → 线上埋点监控,防止性能退化
二、全新开发App:必备配置 & 代码规范(从项目初始化就预埋性能能力)
1. 项目编译&Gradle基础配置(build.gradle)
android {
// 开启代码压缩、资源压缩
buildTypes {
release {
minifyEnabled true // R8代码压缩混淆
shrinkResources true // 移除未使用资源
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
// 资源命名规范,便于后续资源瘦身
resourcePrefix "app_"
// 开启ViewBinding(XML项目),避免findViewById,减少空指针
viewBinding.enabled true
}
- 推荐:使用Kotlin + Jetpack Compose技术栈;架构选择 MVVM / MVI,单向数据流
- 依赖版本统一管理:版本目录
version catalog,避免多依赖版本冲突,减少冗余代码
2. 架构层规范(防性能问题的顶层约束)
- 分层隔离:UI层(Activity/Compose)、ViewModel、Repository、DataSource;网络/DB放在下层,UI层不直接操作IO/网络
- 异步规范:
- 优先Kotlin协程,禁止裸用Thread;使用
CoroutineScope,页面销毁时cancel,防止泄漏 - 网络/数据库/文件IO一律子线程;SP大数据迁移DataStore
- 优先Kotlin协程,禁止裸用Thread;使用
- 组件规范
- 尽量单Activity + 多Fragment / Compose Navigation,减少Activity创建销毁开销
- 路由:ARouter,按需懒加载模块;不要一次性初始化所有路由目标
- 多进程:谨慎使用;多进程之间资源独立,初始化代码做好进程判断,避免重复初始化
3. UI编码规范(XML / Compose)
XML
- 布局扁平化,优先ConstraintLayout;减少嵌套LinearLayout
- 懒加载:ViewStub处理非首屏视图;
<include>复用布局 - 图片:统一图片加载库(Coil/Glide),禁止直接
BitmapFactory.decode在主线程加载大图 - 禁止在onDraw、onLayout中创建对象
Compose 强制规范
- 可组合函数遵守:无副作用、参数稳定;
@Stable标记数据类 - 状态提升,缩小重组范围;列表使用
LazyColumn/LazyRow,不要ScrollColumn加载大量数据 - 列表Item提供稳定key:
items(..., key={it.id}) -
remember只用来缓存昂贵创建对象;derivedStateOf用于派生状态,避免不必要重组 - Modifier顺序规范理解:
padding->background->clickable顺序带来绘制区域差异
4. 内存相关编码规范
- 异步回调、监听注册必须成对反注册;协程作用域页面销毁cancel
- 禁止静态变量持有Activity/Fragment/View/Compose Context;Context使用Application上下文(非UI场景)
- 图片:统一限制采样尺寸,图片内存池;大图分页加载
- 集合:用完清空,避免长生命周期持有大列表;慎用全局静态大集合
5. 启动规范(新建项目最容易踩坑)
- Application 尽量轻量化,只放最基础初始化;所有非核心SDK放到
IdleHandler/ 首屏渲染后
Looper.myQueue().addIdleHandler(object : MessageQueue.IdleHandler {
override fun queueIdle(): Boolean {
// 首屏空闲后初始化非核心SDK
return false
}
})
- 区分主进程/其他进程初始化,用
Process.myPid()判断 - 禁止在静态代码块做耗时任务
6. 预埋性能监控(新项目直接集成)
- 开发阶段:LeakCanary(debug)、BlockCanary(debug)
- 线上:性能SDK埋点(启动耗时、页面帧率、ANR、内存、崩溃)
- 单元测试/自动化:可以编写自动化性能用例,CI打包时检测包体积、启动耗时,超过阈值阻断MR
7. 第三方SDK管理规范
- SDK引入前评估:体积、内存、后台唤醒、WakeLock、线程创建情况
- 非核心SDK支持按需懒加载;不要全部在Application初始化
- 定期审计SDK,移除无用依赖
8. 数据库 & 网络规范
- Room数据库,使用协程挂起函数;批量操作用事务;索引优化;避免查询全表
- 网络:OkHttp单例,连接池复用;统一拦截器处理缓存、重试;批量埋点上报,不要高频单点上报
9. 代码检查静态工具(CI集成)
- Lint:开启,自定义性能相关lint规则(主线程IO、主线程Bitmap加载等)
- Detekt:Kotlin代码静态检查,捕获协程泄漏、不合理作用域
- 提交MR强制Lint检查,不通过禁止合入
三、面试精简版(方便背诵)
存量App优化流程
- 采集性能基线(启动、卡顿、内存、包体积),定位P0/P1问题
- 优先解决启动、ANR、OOM这类高优先级问题;使用Perfetto、BlockCanary、LeakCanary、MAT定位
- 分模块优化:启动懒加载、主线程减负、内存泄漏&抖动、布局/Compose重组优化、包体积瘦身、网络IO耗电优化
- 回归验证,接入线上监控,建立性能门禁防止退化
新项目必备配置&规范
- Gradle开启R8、资源压缩,版本目录管理依赖
- 架构:MVVM/MVI + 分层,Kotlin协程管理异步,页面销毁取消任务防泄漏
- UI:XML扁平化;Compose遵守稳定状态、Lazy列表、remember/derivedStateOf规范
- Application轻量化,非核心能力懒加载,区分进程初始化
- 预埋监控LeakCanary、BlockCanary,CI增加Lint/Detekt、性能基线门禁
- SDK按需引入,IO/网络统一下层,禁止主线程耗时操作