介绍

背景介绍

第一体验
8秒定律

启动分类

App startup time 官方文章

冷启动

热启动

后台->前台

温启动

Activity生命周期

相关任务

冷启动之前

启动app
加载空白Window
创建进程

随后任务

创建Application
创建主线程
创建MainActivity
加载布局
布置屏幕
首帧绘制

优化方向

异步、延迟、懒加载
Application和Activity生命周期

启动时间测量方式

adb命令

adb shell am start -W packagename/首屏Activity(带包名)

信息参数
ThisTime:最后一个Activity启动耗时
TotalTime:所有Activity启动耗时
WaitTime:AMS启动Activity的总耗时

缺点
1.线下方便,不能带线上
2.非严谨精确的时间

手动打点

启动时埋点,启动结束时埋点,二者差值
启动时埋点函数:Application的attachBaseContext函数(我们能感知的Application调用的第一个函数)
启动结束时函数:Activity的onWindowFocusChanged(首帧时间,非数据展示出来的时间,根据需求具体处理)

启动优化工具

traceview

官方文档

特点

图形的形式展示执行时间,调用栈等
信息全面包含所有线程
运行时开销严重
可能会带偏优化方向
traceview与cpu profiler (traceview 可以埋点然后用profiler来分析生成的文件)

使用方式

Debug.startMethodTracing(“”);
Debug.stopMethodTracing();
生成文件在SD卡:sdcard/Android/data/packagename/files

systrace

官方文档
结合Android内核的数据,生成Html报告
API18以上使用,推荐使用他的兼容类 TraceCompat

使用方式
TraceCompat.begainSection(“”);
TraceCompat.endSection();
python2 /Users/fengyang/Library/Android/sdk/platform-tools/systrace/systrace.py -b 32768 -t 10 -a 包名 -o performance.html

wall time 和cpu time

cpu time是优化方向,wall time 需注意线程阻塞
按照systrace及cpu time 跑满cpu

特点

轻量级,开销小
直观反应cpu利用率
cputime与walltime区别
1.walltime是代码执行时间
2.cputime是代码消耗cpu时间(重点指标)
3.举例:锁冲突(例如等待锁,将释放cpu时间片,io不消耗cpu)

优雅获取方法耗时

AOP

简介

Aspect Oriented Programming,面向切面编程
针对同一类问题的统一处理
无侵入添加代码

AspectJ使用

github

简介

AOP编程的一个工具库

集成

classpath ‘com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0’
apply plugin: ‘android-aspectjx’
implementation ‘org.aspectj:aspectjrt:1.8.+’
**

Join Points

程序运行时的执行点,可以作为切面的地方

  • 函数调用执行
  • 获取设置变量
  • 类初始化

PointCut

带条件的JoinPoints

Advice

一种hook,要插入代码的位置

  • Befor:PointCut之前执行
  • After:PointCut之后执行
  • Around:Point之前,之后分别执行

语法介绍

  • Befor:Advice 具体插入位置
  • execution:处理Join Point具体的类型,call 切面在方法调用位置,execution 切在被掉的方法里面 注意:系统类方法只能使用call,无法将代码写入系统类中,只能写入我们工程方法调用处
  • ( com.optimize.performance.PerformanceApp.*(..)):匹配规则
  • getTime:要插入的代码

@Befor(“execution(* com.optimize.performance.PerformanceApp.(..))”)
public void **getTime(ProceedingJoinPoint joinPoint) {

}

异步优化

核心思想:子线程分担主线程任务,并行减少时间
注意点

  • 不符合异步要求(例:不能在子线程中执行)
  • 需要在某阶段完成(例:方法A对方法B的执行结果有依赖,使用多线程同步方案(Thread.wait或者CountDownLatch))
  • 区分CPU密集型和IO密集型任务

启动器流程图

截屏2020-01-14下午7.53.17.png

异步最优方案-启动器

核心思想:充分利用CPU多核、自动梳理任务顺序

启动器流程

  1. 代码Task化,启动逻辑抽象为Task
  2. 根据所有任务依赖关系排序生成一个有象无环图
  3. 多线程按照排序后的优先级依次执行

延迟初始化方案

核心思想:对延迟任务在空闲时间分批初始化
利用IdleHandler特性,空闲执行

其他方案

提前加载SharedPreferences

  • Multidex之前加载,利用此阶段CPU
  • 覆写getApplicationContext()返回this

    启动阶段不启动子进程

  • 子进程会共享主进程CPU资源,导致主进程CPU紧张

  • 注意启动顺序:App onCreate之前是ContentProvider(其启动时间算在application之中的)

    类加载优化:提前异步类加载

  • Class.forName()只加载类本身及其静态变量的引用类

  • new 类实例 可以额外加载类成员变量的引用类
  • 在异步线程中类加载
  • 替换掉系统Classloader,在自定义classloader中打印加载的Class

    启动阶段抑制GC

    CPU锁频

    系统分给我们cpu核心数,但频率不会给我们很高,cpu锁频将cpu频率拉升,系统在打开Activity时会自动帮我们把cpu频率拉升到最高,但会有时间限制

启动优化模拟面试

你做启动优化是怎么做的

分析现状,确认问题: 统计耗时过高,页面启动响应慢 某一版本发现启动很慢,用户反馈多,然后我们分析启动过程,启动流程复杂,主线程执行太多代码逻辑
针对性优化:细查代码主线程任务太多,异步 延迟
长期保持优化效果:启动器,监控

是怎么异步的,异步遇到问题没有

体现演进过程:
普通异步(不够优雅,依赖关系,在特定生命周期之内结束)
介绍启动器 (对任务根据依赖排序,线程数是cpu核心数,保证最大程度利用cpu)

启动优化有哪些容易忽略的注意点

cpu time 和 wall time
注意对延迟初始化的优化(不能在UI显示后直接大量塞代码,需要在主线程空闲时批量执行,以保证主线程的响应速度)
介绍黑科技(提前类加载,拉高cpu核心数和频率)

版本迭代导致的启动变慢你有什么好的方案吗

启动器
结合CI (代码门禁,修改了application代码)
监控完善(每个生命周期加监控,启动变慢可以分析变慢出现在哪个生命周期,对比历史版本变更具体分析解决)