bugreport
adb bugreport
卡顿优化
过度绘制
- 真彩色:没有过度绘制
- 蓝色:过度绘制 1 次
- 绿色:过度绘制 2 次
- 粉色:过度绘制 3 次
- 红色:过度绘制 4 次或更多次
您可以采取以下几种策略来减少甚至消除过度绘制:
- 移除布局中不需要的背景。
- 使视图层次结构扁平化。
- 降低透明度。
GPU 渲染速度
- 沿水平轴的每个竖条代表一个帧,每个竖条的高度表示渲染该帧所花的时间(以毫秒为单位)。
- 水平绿线表示 16 毫秒。要实现每秒 60 帧,代表每个帧的竖条需要保持在此线以下。当竖条超出此线时,可能会使动画出现暂停。
- 每个竖条都有与渲染管道中某个阶段对应的彩色区段。
SysteTrace
原理
APK大小优化
- Proguard
- minifyEnabled true ,shrinkResources true
ANR 参考
触发场景
注
- BroadCast 前台广播10s;后台广播60s
- Service 前台服务20s;后台服务200s
ANR 日志获取
adb root
adb shell ls /data/anr
adb pull /data/anr/<filename>
ANR 常见原因
- 主线程被其他线程锁(占比57%):调用了thread的sleep()、wait()等方法,导致的主线程等待超时。
- 系统资源被占用(占比14%):其他进程系统资源(CPU/RAM/IO)占用率高,导致该进程无法抢占到足够的系统资源。
- 主线程耗时工作导致线程卡死(占比9%):例如大量的数据库读写(I/O),耗时的网络情况,高强度的硬件计算等
ANR 分析思路
- 看主线程(main)堆栈,是否存在明显业务问题(如死锁,业务严重耗时等等)
- Load,进程CPU分布,Slow Operation,Kswapd,mmc-cmdqd,Kwork,Lowmemkiller 等等
- 观察系统整体负载:User,Sys,IOWait
- 观察 Top 进程的 CPU 占比。CPU使用很少,可能是主线程阻塞了
- 看 CPU 占比定线程 :对比各线程 CPU 占比,以及线程内部 user 和 kernel 占比
指标关键字
- Load 系统负载,发现 ANR 前 1,前 5,前 15 分钟(Load:7.45 / 6.92 / 6.84)
- CPU 使用率:CPU usage from 0 ms to 8745ms later (later:表示发生 ANR 后一段时间各进程 CPU 占比;ago 表示:ANR 之后一段时间,各进程 CPU 占比)
- 对应用来说 kernel 空间 CPU 占比较高,说明应用侧应该发生了大量的系统调用,对普通应用来说,发生系统调用的多数场景都是文件 IO 操作。
- kswapd,mmc 整体内存和 IO 负载。
- Trace 中各线程耗时(utm+stm)
- kworker 属于内核线程,当 IO 负载过重时会在该线程有所体现
- ANR 时看到的 MessageQueue.NativePollOnce 场景的问题,第一判断依然是主线程历史消息耗时严重,或者系统负载过重导致的问题
疑难解决
- 解决 SharedPreference apply 引起的 ANR 问题
原因: SP 调用 apply 方法,会创建一个等待锁放到 QueuedWork 中,并将真正的数据持久化封装成一个任务放到异步队列中执行,任务执行结束会释放锁。Activity onStop 以及 Service 处理 onStop,onStartCommand 时,执行 QueuedWork.waitToFinish() 等待所有的等待锁释放。
解决办法:所有此类 ANR 都是经由 QueuedWork.waitToFinish() 触发的,只要在调用此函数之前,将其中保存的队列手动清空即可。
commit 方式会阻塞调用的线程.apply 放法不会阻塞调用的线程.
Crash 优化
应收集的信息
- 进程名、线程名、堆栈和类型
- 机型、系统
- 剩余内存。当系统可用内存小于MemTotal的10%时,OOM、大量GC、系统频繁自杀拉起等问题非常容易出现。
- 内存资源足够,但内存还分配失败,可能是资源泄露。
- 崩溃场景、关键操作路径
分析流程
- Java崩溃:如果是OOM,需进一步查看日志中的内存信息和资源信息
- Native崩溃:查看signal、code、fault addr以及崩溃时的Java堆栈
- 常见 SIGSEGV:空指针、非法指针等。SIGABRT:ANR、调用abort推出等
- 找对应机型、系统版本复现
疑难解决
- Android 7.0 Toast BadTokenException
参考Android 8.0 try catch的做法,代理Toast里的mTN(handler)就可以实现捕获异常
- 解决TimeoutExceptin异常
attachBaseContext()方法中,通过反射来停止FinalizerWatchdogDaemon的方式来解决问题
- 输入法InputMethodManager的内存泄漏
解决办法原理简单粗暴,直接把mLastSrvView等置空,破坏掉path to gc节点
功耗优化
低耗电和应用待机模式
- 低耗电:未插电源,屏幕关闭下,设备一段时间内不活动,设备就会进入低电耗模式。在低电耗模式下,延迟应用的后台 CPU 和网络活动。系统会定期退出低电耗模式一小段时间,让应用完成其延迟的活动。一旦用户通过移动设备、打开屏幕或连接至充电器唤醒设备,系统就会立即退出低电耗模式,并且所有应用都会恢复正常活动。
- 应用待机: 延迟用户近期未与之交互的应用的后台网络活动
耗电场景
- 唤醒屏幕:
- 屏幕渲染和长时间运行CPU
- 蜂窝式无线网路
- 传感器(GPS等)
优化建议
- 减少屏幕唤醒次数,使用WorkManager处理后台任务 参考
- 大量计算放在服务器,来减少CPU运行时间。屏幕绘制优化,减少渲染次数和频率。
- 合并网络请求,缓存请求结果,来减少使用网络次数。无网络不请求。
- GPS不使用高精度GPS_PROVIDER定位,而采用NETWORK_PROVIDER 移动网路基站和Wifi获取位置,同时减少定位次数,及时注销监听。其他传感器采用合适的采样率。
Battery-Historian 采集和分析
- 数据采集
1.重置数据 adb shell dumpsys batterystats --reset
2. 操作APP,并后台运行一段时间
3. 导出报告 adb bugreport bugreport.zip
- 数据分析
上传到 bathist.ef.lc/ 分析报表
内存优化
参考 内存优化能让应用挂得少、活得好和活得久
- 挂的少,减少OOM,Crash
- 减少GC次数,提高流畅度
- 内存占用少,后台运行时,不容易被杀死
gc原理
可达性分析算法
C Roots(每种具体实现对GC Roots有不同的定义)作为起点,向下搜索它们引用的对象,可以生成一棵引用树,树的节点视为可达对象,反之视为不可达
GC Roots对象
- 虚拟机栈(帧栈中的本地变量表)中引用的对象。
- 本地方法栈中JNI引用的对象。
- 方法区中静态属性引用的对象。
- 方法区中常量引用的对象。
不同引用的回收
- 引用 当内存不足,宁可抛出OOM异常也不回收。
- 软引用 (SoftReference),如果内存空间不足了,就会回收这些对象的内存
- 弱引用 (WeakReference),一旦发生GC,就容易被回收。
- 虚引用 (PhantomReference),它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收
分析内存
- adb dumpsys meminfo
- profile ,Shalow Size 对象本身大小;Retained Size GC能回收走的大小
- MAT
- LeakCanary
内存泄漏
内存泄漏指的是程序不再使用的对象无法被GC识别回收,导致这个对象一直留在内存当中
长生命周期的对象持有短生命周期对象的强引用,在短生命周期对象需要回收的时候发现不能被回收,视为泄漏。
解决办法:
- context 尽量使用 ApplicationContext
- 取消注册的监听
- onDestory时,清除Timer,清空Hanlder消息队列,Handler使用静态内部类 + Activity弱引用的方式.
- Cursor忘了关闭
- 集合对象没有及时清理引起的内存泄漏
内存抖动
频繁的分配与回收,(分配速度大于回收速度时) 最终产生 OOM
解决办法:
- 不在for循环中new 对象
- 不在for循环中用+号拼接字符串,使用StringBuilder
- onDraw() 中不要创建对象
- 大量使用Bitmap的时候,试着把它们缓存在数组中实现复用
内存优化技巧
- 谨慎使用Service,可使用IntentSerice
- 选择优化后的数据容器。 SpareArray,LongSpareArray等
- protobuf 作为序列化数据,而不是xml
- 图片根据使用的到大小裁剪、压缩