Android 性能优化不是上线前临时跑一次 Profiler,也不是看到卡顿后盲目改几行代码。真正稳定的做法,是把性能问题拆成可观测、可复现、可验证的工程流程:先知道慢在哪里,再判断为什么慢,最后用指标证明优化确实有效。
一、先定义性能目标
性能优化最容易踩的坑,是一开始就讨论“怎么优化”,却没有定义“优化到什么程度”。
在项目里可以先固定几类指标:
- 启动耗时:冷启动、温启动、首屏可交互时间。
- 页面流畅度:掉帧、长帧、列表滑动是否稳定。
- 内存表现:峰值内存、频繁 GC、大图和缓存占用。
- 网络链路:接口耗时、重试、弱网下的用户等待时间。
- 包体体积:APK/AAB 大小、so、资源、重复依赖。
- 耗电和后台任务:定位、轮询、WorkManager、前台服务。
指标不需要一开始就非常复杂,但必须能被重复测量。否则优化结果只能依赖主观体感,很难在版本迭代中持续保持。
二、启动优化:减少主线程和首屏负担
启动慢通常不是单点问题,而是一组初始化逻辑叠加后的结果。
常见问题包括:
Application.onCreate()初始化过多 SDK。- 主线程做磁盘 IO、数据库打开、JSON 解析。
- 首屏布局层级过深或包含复杂自定义 View。
- 首页一次性请求太多接口。
- 启动时加载大图、字体、动态配置。
实践上可以按优先级处理:
- 只保留首屏必须初始化的逻辑。
- 非必要 SDK 延迟到用户真正使用前。
- IO 操作放到后台线程,并控制结果回主线程的时机。
- 首页数据分层加载,先展示骨架或缓存,再刷新网络数据。
- 用 Baseline Profile 优化核心启动路径和高频页面路径。
一个简单的延迟初始化示例:
1 | class App : Application() { |
这里的关键不是“全部异步”,而是明确哪些初始化真的影响首屏,哪些可以后移。如果把所有任务同时丢到线程池,也可能造成 CPU 争抢,反而影响启动。
三、卡顿优化:把主线程当成稀缺资源
Android 的 UI 渲染依赖主线程协作完成。主线程被长时间占用,就会出现掉帧、点击无响应和动画不顺滑。
排查卡顿时可以重点看:
- 主线程是否有耗时方法。
- RecyclerView 是否频繁创建对象。
- 图片解码是否发生在主线程。
onBindViewHolder()是否做了复杂计算。- 自定义 View 的
onDraw()是否分配对象。 - 是否存在同步锁竞争。
列表页面是最常见的卡顿来源。优化时可以先从绑定逻辑入手:
1 | class ArticleAdapter : ListAdapter<Article, ArticleViewHolder>(DIFF) { |
列表绑定中应该避免:
- 每次绑定都格式化复杂时间。
- 每次绑定都创建新的正则、画笔、监听器。
- 在绑定阶段同步查询数据库。
- 在绑定阶段直接解码 Bitmap。
- 使用
notifyDataSetChanged()刷新整个列表。
如果数据量较大,优先使用 ListAdapter、DiffUtil 或 Paging,让刷新范围尽可能小。
四、内存优化:重点治理泄漏和大对象
内存问题的表现不一定是立刻崩溃,更多时候是页面越用越卡、GC 频繁、图片列表抖动。
常见风险包括:
- Activity 或 Fragment 被单例、回调、Handler 持有。
- 协程作用域超过页面生命周期。
- Bitmap 缓存没有边界。
- WebView、播放器、地图组件释放不彻底。
- 大列表一次性加载完整数据。
页面里的协程建议绑定生命周期:
1 | class ArticleFragment : Fragment(R.layout.fragment_article) { |
这里使用 viewLifecycleOwner,可以避免 Fragment View 销毁后仍继续更新旧 View。对于 ViewBinding,也要在 onDestroyView() 里释放引用。
1 | override fun onDestroyView() { |
如果项目里图片很多,需要检查图片加载库的内存缓存、磁盘缓存、缩略图策略和列表预加载策略。大图展示页最好按目标尺寸加载,不要把原图完整解码到内存。
五、网络优化:减少用户等待
网络性能不只看接口平均耗时,还要看弱网、失败重试和页面串行等待。
可以从几个方向入手:
- 合并首屏必要接口,减少串行请求。
- 非关键数据延迟加载。
- 对稳定数据做本地缓存。
- 请求失败时保留旧数据,而不是清空页面。
- 给长请求设置合理超时。
- 避免重复点击触发重复请求。
Repository 层可以统一处理缓存和刷新:
1 | class UserRepository( |
UI 先展示本地数据,再触发远端刷新。这样即使网络较慢,用户也不会一直面对空白页面。
六、布局优化:少嵌套,少重复测量
布局性能问题常出现在复杂首页、动态表单、嵌套列表和弹窗里。
建议优先检查:
- 是否存在没有必要的多层嵌套。
- ConstraintLayout 是否能替代多层 LinearLayout。
- RecyclerView 是否嵌套 RecyclerView。
- 图片是否设置了明确尺寸。
- 文本是否因为宽高不稳定导致反复测量。
- 是否过度使用
wrap_content造成额外测量成本。
优化布局时不要只追求“层级越少越好”,还要保持结构可维护。一个清晰的布局,比为了少一层而写出难维护的约束关系更可靠。
七、包体优化:从依赖和资源开始
包体变大通常来自三类内容:依赖、资源、Native 库。
可以固定做这些检查:
- 开启 R8 和资源压缩。
- 删除无用图片、无用语言包、重复资源。
- 检查第三方 SDK 是否引入过多传递依赖。
- 按 ABI 控制 so 输出。
- 大资源放到服务端或动态下发。
- 使用矢量图替代简单 PNG。
Gradle 中常见配置:
1 | android { |
包体优化要配合回归测试。尤其是混淆和资源压缩,必须覆盖反射、WebView JS 调用、路由跳转、序列化等路径。
八、建立日常治理机制
性能优化最怕“一次性运动”。真正有效的方式,是把性能指标放进日常开发流程。
可以建立一套轻量机制:
- 每次发版前记录启动耗时、包体大小、核心页面帧率。
- 新增重型 SDK 必须说明初始化时机和包体影响。
- 大图、长列表、复杂自定义 View 合入前做专项检查。
- 对启动链路和首页链路做基准测试。
- 线上采集卡顿、ANR、OOM 和慢请求。
- 性能问题进入缺陷管理,而不是只在群里口头提醒。
对于团队来说,性能优化的目标不是让某一次测试数据漂亮,而是让性能退化能被及时发现、及时止损。
九、一个实用排查顺序
如果接手一个已经变慢的 Android 项目,可以按这个顺序推进:
- 先测冷启动、首页加载、核心列表滑动、内存峰值。
- 用 Profiler、Perfetto 或系统 Trace 找主线程长任务。
- 清理
Application中非必要初始化。 - 优化首页数据加载顺序。
- 排查列表绑定、图片加载和局部刷新。
- 用 LeakCanary 检查核心页面泄漏。
- 分析 APK,处理明显的大资源和冗余依赖。
- 把关键指标写入发版检查清单。
这个顺序的好处是先处理用户最容易感知的问题,再处理长期治理问题。每一步都能产生可验证的结果。
总结
Android 性能优化的核心是工程化,而不是技巧堆叠。
启动、卡顿、内存、网络、布局、包体都可以优化,但优先级应该由用户体验和数据决定。先建立指标,再定位瓶颈,最后用测试和线上监控确认效果。只有这样,性能优化才不会停留在“感觉变快了”,而是变成项目可以长期复用的能力。
- 本文链接: https://blog.hansong.icu/2026/06/26/Android_Performance_Optimization_Practice_2026_06_26/
- 版权声明: 本博客所有文章除特别声明外,均默认采用 CC BY-NC-SA 4.0 许可协议。