性能优化(下):启动优化、网络优化、电量优化与渲染优化
好,咱们接着聊性能优化。上一章我把内存和布局优化讲得差不多了,这一章咱们聚焦在另外四个关键点上:启动、网络、电量和渲染。说实话,这几个点在实际项目中踩坑的概率非常高。我当年刚带项目时,就因为在启动优化上偷了懒,结果被用户骂“打开慢得像老年机”。嗯,从那以后,我再也不敢小看任何一项优化了。
一、启动优化:冷启动 vs 热启动
先搞清楚概念。冷启动,指的是进程从零开始创建,Application 和 Activity 都要重新走一遍生命周期。热启动呢?Activity 被回收了但进程还在,只需要重新创建 Activity 就行。说白了,冷启动是“从头再来”,热启动是“半路接手”。
我个人习惯把冷启动的耗时拆成三个阶段:
- Application 初始化:onCreate 里干了多少脏活累活?
- Activity 创建:onCreate、onStart、onResume 里有没有阻塞主线程?
- 首帧渲染:第一帧画面什么时候交给屏幕?
你想想看,很多团队一上来就往 Application 里塞 SDK 初始化,什么推送、统计、地图、IM……全堆在一起。结果呢?冷启动直接奔着两三秒去了。我建议的做法是:能懒加载就懒加载,能异步就异步。
核心原则:Application.onCreate 里只做最核心的初始化(如崩溃收集、性能监控),其余全部推迟到使用时再初始化。
举个例子,我之前接手过一个项目,Application 里初始化了 12 个 SDK。我改成按需加载后,冷启动时间从 2.8 秒降到了 1.1 秒。怎么做的?用 IdleHandler 或者 WorkManager 延迟执行。
// 利用 IdleHandler 在主线程空闲时执行非关键初始化
Looper.myQueue().addIdleHandler(() -> {
// 初始化非关键 SDK
initNonCriticalSDKs();
return false; // 只执行一次
});
小技巧:启动页不要用纯白或纯黑背景,用一张和 App 主题色一致的启动图(windowBackground),用户感知上会觉得“秒开”。
二、网络优化:缓存与压缩
网络优化,说白了就是两件事:少发请求和少传数据。
先聊缓存。我见过不少团队,每次打开页面都重新请求接口,哪怕数据根本没变。这其实很浪费流量和电量。我个人习惯用 OkHttp 的缓存策略,配合服务端返回的 Cache-Control 头,能省掉大量重复请求。
// OkHttp 缓存配置示例
int cacheSize = 10 * 1024 * 1024; // 10 MB
Cache cache = new Cache(cacheDir, cacheSize);
OkHttpClient client = new OkHttpClient.Builder()
.cache(cache)
.addInterceptor(new CacheInterceptor()) // 自定义缓存拦截器
.build();
再聊压缩。图片是流量大户。我建议所有图片都走 WebP 格式,体积比 PNG 小 30% 以上。接口返回的 JSON 数据,可以开启 Gzip 压缩。OkHttp 默认支持 Gzip,但记得确认服务端也开了。
注意:缓存不是万能的。对于实时性要求高的数据(如订单状态、聊天消息),必须走实时请求,不能依赖缓存。我曾经因为缓存策略没配好,导致用户看到的是半小时前的订单状态,差点被投诉到应用商店下架。
三、电量优化:JobScheduler 与 WorkManager
电量优化,很多开发者容易忽略。但你想,用户手机电量就那么多,你的 App 在后台偷偷跑网络、频繁唤醒 CPU,用户不卸载你卸载谁?
Android 官方推荐用 WorkManager 来管理后台任务。它内部会根据系统版本自动选择 JobScheduler(API 21+)或 AlarmManager,而且能自动处理 Doze 模式和 App Standby。
// WorkManager 示例:延迟执行后台同步任务
OneTimeWorkRequest syncWork = new OneTimeWorkRequest.Builder(SyncWorker.class)
.setInitialDelay(10, TimeUnit.MINUTES)
.setConstraints(new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build())
.build();
WorkManager.getInstance(context).enqueue(syncWork);
我个人习惯把非紧急的后台任务(如日志上传、数据预加载)都交给 WorkManager。它会在合适的时机(比如充电时、WiFi 下)才执行,对电量非常友好。
避坑指南:我曾经在项目里直接用 AlarmManager 做定时任务,结果在 Android 8.0 以上频繁被系统限制。后来全部迁移到 WorkManager,问题迎刃而解。记住:能用 WorkManager 就别自己造轮子。
四、渲染优化:Profile GPU 工具
渲染优化,核心目标是保证 60fps,也就是每帧 16.6ms 内完成绘制。如果超了,用户就会感觉到卡顿。
Android 提供了 Profile GPU Rendering 工具,可以在开发者选项里打开。它会用柱状图显示每帧的绘制耗时,绿色代表正常,红色代表超时。
我一般这样分析:
- 蓝色柱太高:说明 View 的 onDraw 里做了太多绘制操作,比如重复绘制、复杂 Path。
- 红色柱太高:说明 GPU 处理时间过长,通常是过度绘制(Overdraw)导致的。
- 黄色柱太高:说明 CPU 在等待 GPU 完成上一帧,可能是布局层级太深。
你想想看,如果一帧里红色柱占了 20ms,那这帧肯定掉帧了。我遇到过一个案例,一个列表页的 item 布局嵌套了 7 层,每层都有背景色,结果过度绘制严重。我把它压到 3 层,去掉不必要的背景,帧率直接从 40fps 升到了 58fps。
小技巧:用 GPU Overdraw 工具(开发者选项里也有)查看过度绘制区域。蓝色最好,红色要警惕。我一般要求团队把红色区域控制在屏幕面积的 10% 以内。
五、Systrace 工具使用
Systrace 是性能优化的“核武器”。它能记录系统级和 App 级的耗时事件,精确到微秒级别。我个人习惯在排查卡顿、启动慢、ANR 问题时,第一个想到的就是 Systrace。
使用方法很简单:
- 打开开发者选项,开启 System Tracing。
- 复现问题场景,然后停止录制。
- 用 Chrome 浏览器打开生成的
.trace文件。
在 Systrace 里,你会看到一条条彩色长条,每个长条代表一个线程或进程的活动。重点关注:
- 主线程(UI Thread):有没有长时间占用?有没有被 Binder 调用阻塞?
- SurfaceFlinger:帧率是否稳定?有没有掉帧?
- CPU 频率:有没有降频?降频会导致性能下降。
我记得有一次,用户反馈某个页面滑动卡顿。我用 Systrace 一抓,发现主线程每隔 2 秒就执行一次 200ms 的磁盘写入操作。原来是某个第三方 SDK 在后台频繁写日志。我找到 SDK 厂商,让他们改成异步写入,卡顿就消失了。
注意:Systrace 在 Android 10 以上已经被 Perfetto 取代,但原理是一样的。如果你用的是新设备,建议直接学 Perfetto。不过老项目还是 Systrace 更通用。
知识体系总览
下面这张图,我把本章的核心知识点串起来了。你可以把它当作一个检查清单,做优化时对照着看。
好了,这一章的内容就到这里。性能优化没有银弹,关键是要养成用工具说话的习惯。下次遇到卡顿、耗电、启动慢,别靠猜,先上 Systrace 和 Profile GPU 看看数据。数据会告诉你问题在哪。
公众号:蓝海资料掘金营,微信deep3321