2.39 Android性能优化笔记
2.39.1 启动加速
- 冷启动(App在设备刚启动或者应用被kill后首次启动即冷启动)
- 冷启动的开始, 系统会执行以下三个任务
- 加载启动App
- 启动后显示空白window
- 创建app的process
- 创建app的process后马上执行以下任务
- 创建App对象
- 启动main thread
- 创建Main Activity
- inflating views
- Laying out the screen
- 执行首次draw(用户可以使用app)
- 问题: 1).
Application#onCreate()
负载过重, 2).Activity
创建, 包含初始化values, 调用构造方法, 调用Activity
生命周期方法
- 冷启动的开始, 系统会执行以下三个任务
-
热启动, 进程和
Activity
未被kill掉, 系统把App的Activity
重新拿到前台展示 -
暖启动, 1). 用户返回并重新启动, 当进程可运行而
Activity
需要重新创建; 2). 系统清除应用内存, 进程和Activity
需要重新启动 -
优化: 1). 避免在
Application#onCreate
或者MainActivity
过重的初始化; 2). 定位问题: 避免I/O操作, 反序列化, 网络操作和布局嵌套, 3). 尽可能利用虚拟文件系统的page cache, 减少IO, 查找发生IO的文件,按时间顺序统计启动阶段的所有文件,排布在一起,这样发生少量 IO,就能全部读到 cache 中 安装包重排布 - 排查方法
- ADB命令: ` adb shell am start -W xxx/.XXXActivity
, 该指令给出三个时间, 1). ThisTime: 最后一个启动
Activity`的启动耗时, 2). TotalTime: 自己的所有Activity的启动耗时, 3). WaitTime: AMS启动App的Activity的总时间 - TraceView
- ADB命令: ` adb shell am start -W xxx/.XXXActivity
布局优化
- OverDraw(过度绘制)
- 多层布局设置背景色导致OverDraw
- 嵌套层次太深
- 优化策略
- 减少重复设置背景色
- 优化
View
的层级嵌套 merge
标签, 可以合并布局, 减少布局的层级merge
多用于顶替顶层FrameLayout
或者include
布局时, 消除因为引用布局导致的多余嵌套ViewStub
标签, 延迟创建或初始化, 需要调用ViewStub#inflate()
或者ViewStub#setVisibility()
显示- 自定义控件, 在
onDraw
不能进行复杂运算
- 排查方法
- Hierarchy View工具可排查View的层级以及绘制流程的耗时
- TraceView
- 开发者选项->调试GPU过度绘制->显示过度绘制区域
- UIAutoMatorViewer, 只能查看布局层次
内存优化
- 常见内存泄露
- handler内存泄露(匿名内部类)
- TimerTask内存泄露(匿名内部类)
- 资源性对象未关闭(SQLite的Cursor或File)
- 系统源码泄露, 如
InputManager
, 或错误使用其它如WiFiManager
- 非静态内部类(持有外部类引用)
- WebView
- OOM问题
- Java堆内存溢出, 为对象分配内存时达到进程的内存上限。由Runtime.getRuntime.MaxMemory()可以得到Android中每个进程被系统分配的内存上限,当进程占用内存达到这个上限时就会发生OOM
- 无足够连续内存空间
- FD数量超出限制
- 线程数量超出限制
- 虚拟内存不足
- 优化策略
- 节制使用
Service
- 使用优化的集合, 如
SparseArray
等 - 少用枚举
- 使用protobuf序列化数据
- 避免内存抖动
- 节制使用
- 排查方法
- MAT
- Histogram, 可以列出内存中每个对象的名字, 数量和大小
- Dominator Tree会将所有内存中的对象按大小进行排序, 帮助分析对象直接的引用结构
- Incoming Reference: 对象A它被谁引用, 列举引用引用对象A的引用
- Outgoing Reference: 对象A它引用谁, 列举A对象引用
- 排查: 1). 对着想查看的对象点击右键 -> Lists objects -> with incoming references 2). 右键 -> Path To GC Roots -> exclude weak reference
- leakcanary
- MAT
- Bitmap内存
- Bitmap复用: 1). 使用
LruCache
或DiskLruCache
做内存或磁盘缓存; 2). 使用Bitmap复用, 使用inBitmap
属性 - Bitmap压缩: 1).
Bitmap#compress()
, 只压缩大小, 不影响内存大小, 2).BitmapFactory.Options.inSampleSize
- Bitmap复用: 1). 使用
卡顿
- 常见卡顿
- UI线程耗时操作, 如UI线程的I/O读写, 数据库访问等
- 复杂不合理的布局和OverDraw
- 内存泄露/抖动导致的卡顿
- 错误的异步方式
- 排查方法
StrictMode
- TraceView
- AndroidPerformanceMonitor, ANR-WatchDog, Matrix
- 优化策略
- 将UI耗时放到异步
- 合理优化布局, 避免OverDraw
- 优化内存使用
- 正确使用异步
ANR
- ANR类型
- KeyDispatchTimeout, 5S无响应
- BroadcastTimeout, 10秒无响应
- ServiceTimeout, 20秒无响应
- ContentProviderTimeout, 10秒无响应
- 常见ANR
- 主线程阻塞, IOWait, 死锁
- IPC通信, 对端长时间无响应
- 其它线程CPU占用率高
- 排查方法
trace.txt
+ logcat- dumpsys dropbox
网络优化
- 优化策略
- GZIP压缩(减少流量消耗, 减少传输时间)
- IP直连与HttpDns(解决DNS解析慢和解析失败的问题)
- 图片处理, 1). 图片下载, 使用WebP, 对比JPG, 流量减少25%-35%, 对比PNG能减少80%, 且图片质量未下降, 2). 图片上传
- 协议优化, Http1.1引入持久连接, 多请求复用等, http2引入多工, 头信息压缩, 服务器推送等
- 请求打包, 合并网络请求, 减少请求次数
- 网络缓存
- 断点续传, protocol buffer
电量
- 排查
- 注册广播, 与
BatteryManager
交互 - Battery Historian, 先通过adb导出电量信息,
adb shell dumpsys batterystats --reset
- 注册广播, 与
APK瘦身
- 排查
- Android Studio Analyse APK
- ClassShark
- Nimbledroid
- 优化策略
- 代码层面: 1). 移除无用代码, 功能, 2). 移除无用的库, 避免功能雷同的库, 3). 启动混淆, 4). 缩减方法数
- 资源层面: 1). 移除无用的资源文件, 2). drawable目录只保留一份资源, 3). 对图片压缩, 4). 使用WebP, 5). 资源混淆
- SO层面: 1). 针对用户机型分部保留特定架构的so
- dex中优化debug items字段