2.39 Android性能优化笔记

1 minute read

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

布局优化

  • 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
  • Bitmap内存
    • Bitmap复用: 1). 使用LruCacheDiskLruCache做内存或磁盘缓存; 2). 使用Bitmap复用, 使用inBitmap属性
    • Bitmap压缩: 1). Bitmap#compress(), 只压缩大小, 不影响内存大小, 2). BitmapFactory.Options.inSampleSize

卡顿

  • 常见卡顿
    • 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字段

参考