Android 性能优化是一个系统性工程,涵盖多个维度。以下是核心优化方向的详细解析:
一、内存优化
1.1 内存泄漏排查
| 泄漏类型 | 常见场景 | 解决方案 |
|---|---|---|
| 静态引用 | static 持有 Activity/Context |
使用 WeakReference,避免静态持有 |
| 匿名内部类 | Handler、AsyncTask 持有外部类引用 |
使用静态内部类 + WeakReference |
| 资源未释放 | Bitmap、Cursor、Stream 未关闭 |
try-finally 或 try-with-resources |
| 监听器未注销 | BroadcastReceiver、LocationListener |
在 onDestroy() 中注销 |
| 单例模式滥用 | 单例持有 Activity 引用 |
传入 ApplicationContext |
1.2 内存抖动(Memory Churn)
-
现象:频繁创建和销毁对象,导致 GC 频繁触发,UI 卡顿
-
优化手段:
-
对象池复用(如
Message.obtain()、RecyclerView的 ViewHolder) -
避免在
onDraw()/onMeasure()中创建对象 -
使用
StringBuilder替代字符串拼接 -
使用
SparseArray替代HashMap<Integer, Object>
-
1.3 Bitmap 优化
java
// 1. 采样率压缩
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2; // 宽高各压缩为 1/2
Bitmap bitmap = BitmapFactory.decodeResource(res, resId, options);
// 2. 复用内存(Android 3.0+)
options.inBitmap = reusedBitmap;
// 3. 使用合适的图片格式
// ARGB_8888 (4字节/像素) → RGB_565 (2字节/像素,无透明)
1.4 大对象与内存监控
-
使用 Profiler 或 LeakCanary 检测泄漏
-
关注
LargeHeap属性(谨慎使用) -
使用
onTrimMemory(int level)释放缓存
二、UI 渲染优化
2.1 渲染管线与 16ms 原则
Android 每帧需在 16ms 内完成(60fps),否则出现掉帧。
plain
CPU (Measure → Layout → Draw) → GPU (Rasterization → Display)
2.2 过度绘制(Overdraw)
-
检测:开发者选项 → 调试 GPU 过度绘制
-
优化:
-
移除不必要的背景(
windowBackground与根布局背景重复) -
使用
<merge>标签减少层级 -
使用
clipRect()和quickReject()裁剪不可见区域 -
自定义 View 时避免在
onDraw()中绘制被遮挡的部分
-
2.3 布局层级优化
| 方案 | 说明 |
|---|---|
| ConstraintLayout | 扁平化布局,替代多层嵌套 |
| ViewStub | 按需加载(延迟初始化) |
<include> + <merge> |
复用布局,减少层级 |
避免使用 RelativeLayout |
性能较差,已废弃 |
2.4 列表优化(RecyclerView)
-
使用
DiffUtil局部更新,避免notifyDataSetChanged() -
设置
setHasFixedSize(true)优化布局计算 -
图片加载使用占位图 + 三级缓存
-
分页加载(Paging 3)
三、启动优化
3.1 冷启动 vs 热启动
plain
冷启动:App 进程不存在 → 创建进程 → Application → Activity 创建
热启动:进程存在,Activity 被回收后重建
3.2 启动优化策略
-
异步初始化 :使用
Coroutine/HandlerThread将非必要初始化移出主线程 -
延迟加载 :使用
IdleHandler在主线程空闲时执行 -
ContentProvider 优化 :避免在
Application.onCreate()前执行耗时操作 -
布局预加载 :使用
ViewStub或AsyncLayoutInflater -
Multidex 优化 :Android 5.0 以下使用
MultiDex,考虑代码拆分
3.3 启动时间测量
bash
adb shell am start -W com.package.name/.MainActivity
# 关注 TotalTime(从 AMS 到 Activity 显示)
四、电量优化
4.1 Doze 模式与 App Standby
-
Doze 模式:设备静止且未充电时,延迟后台任务
-
App Standby:长期未使用的 App 限制后台活动
4.2 后台任务优化
表格
| 场景 | 推荐方案 |
|---|---|
| 定时任务 | WorkManager(兼容 Doze) |
| 网络请求 | JobScheduler / WorkManager |
| 定位 | 降低更新频率,使用 FusedLocationProvider |
| 唤醒锁 | 避免滥用 WakeLock,使用 WakefulBroadcastReceiver |
4.3 传感器与硬件
-
及时注销传感器监听(
SensorManager.unregisterListener) -
使用
JobScheduler批量网络请求 -
避免频繁使用
AlarmManager(使用setAndAllowWhileIdle)
五、APK 体积优化
5.1 代码层面
-
ProGuard/R8:代码混淆、移除无用代码
-
资源缩减 :
shrinkResources true -
移除多语言支持 :
resConfigs "zh", "en" -
使用 Android App Bundle (AAB):动态交付
5.2 资源层面
| 资源类型 | 优化方案 |
|---|---|
| 图片 | WebP 格式(比 PNG 小 25-35%)、Vector Drawable |
| 音频 | 使用 OGG 替代 WAV |
| 字体 | 仅包含需要的字重,使用可下载字体 |
| 原生库 | abiFilters 过滤不需要的 ABI(如 arm64-v8a) |
六、网络优化
6.1 请求优化
-
连接复用:HTTP/2 多路复用、Keep-Alive
-
请求合并:批量接口替代多次单条请求
-
数据压缩:Gzip、Protocol Buffers
-
缓存策略 :
OkHttp缓存、Room本地持久化
6.2 弱网优化
-
超时重试机制(指数退避)
-
图片降级(弱网加载低分辨率图)
-
失败队列 + 网络恢复后重试
七、存储与 I/O 优化
7.1 SharedPreferences 优化
-
问题 :
commit()同步写磁盘、全量替换 XML -
替代方案:
-
DataStore(Kotlin 协程,类型安全) -
MMKV(腾讯开源,mmap 内存映射,性能高 100 倍)
-
7.2 数据库优化
-
使用索引加速查询
-
批量操作使用事务(
beginTransaction()) -
使用
Room的@Fts4全文搜索 -
避免主线程查询(
allowMainThreadQueries()仅调试)
7.3 文件操作
-
使用
BufferedInputStream/BufferedOutputStream -
大文件使用
MappedByteBuffer(内存映射) -
避免在
SharedPreferences中存储大对象
八、卡顿与 ANR 分析
8.1 ANR 触发条件
-
主线程 5 秒内未响应输入事件
-
BroadcastReceiver10 秒内未完成 -
Service20 秒内未执行完onStartCommand()
8.2 分析工具
| 工具 | 用途 |
|---|---|
| Systrace | 分析 CPU、渲染、IO 等系统级耗时 |
| Profiler (Android Studio) | CPU、Memory、Network、Energy 实时监控 |
| Choreographer | 帧率监控:Choreographer.getInstance().postFrameCallback() |
| StrictMode | 检测主线程 IO、内存泄漏 |
九、架构层面的优化
9.1 模块化与组件化
-
按业务拆分模块,减少编译时间
-
使用
AAR依赖替代源码依赖 -
路由解耦(ARouter 等)
9.2 线程管理
-
使用线程池替代裸线程(
Executors、ThreadPoolExecutor) -
避免线程泄漏和线程数爆炸
-
Kotlin 优先使用
Coroutine(轻量级线程)
十、性能优化检查清单
□ 使用 LeakCanary 检查内存泄漏
□ 使用 Profiler 分析 CPU 和内存热点
□ 检查过度绘制(GPU 过度绘制调试)
□ 确保列表使用 RecyclerView + ViewHolder
□ 启动时间 < 1.5s(冷启动)
□ 图片使用 WebP + 采样压缩
□ 后台任务使用 WorkManager
□ 使用 DataStore/MMKV 替代 SP
□ 启用 R8 代码压缩和资源缩减
□ 使用 ConstraintLayout 减少布局层级