App Performance Optimisation Startup - inputmethod/PinyinIME GitHub Wiki

App启动优化

概述 背景:第一印象,等待时间与用户留存(等待8s) 启动分类:冷启动 - 从0开始,热启动 - 从后台切换到前台,温启动 - 走lifecycle (官方 App startup time) => 整体流程 -> 优化方向 相关任务:启动前系统行为(启动app, 加载空window,创建进程)干预有限,然后创建Application类,启动UI主线程,创建MainActivity(或者MainService),最后加载布局,布置屏幕,首帧绘制。(从用户体验角度说,首帧绘制后到绘出屏幕元素这段时间也涵盖到) 优化方向:Application和Activity或Service生命周期

时间测量 方法:adb命令行显示Activity启动时间(如何测Service如输入法), 手工打点记时。(精确度,线上使用)第一个ui数据展示,addOnDrawListener(API16)或addOnPreDrawListener

启动优化工具 TraceView: 图形化展示执行时间和调用栈,包含所有线程的信息(全面) Debug.startMethodTracing("filename.trace") Debug.stopMethodTracing(); sd:/Android/data/xxxpackagename/filename.trace Android Studio: Device File Explorer (左下角图标弹出选项之一) Call chart, Frame chart, Top down, Bottom up 线程 - 方法时间:total = self time + children time; Wall clock time > Thread time(CPU执行时间) 工具本身开销(大,x10+, 对个别方法可能成为噪音,带偏优化方向,vs CPU Profiler)

System Trace 结合Android内核数据生成html报告,API18+ -> TraceCompat(向下兼容)

python systrace.py -t 10 ..... (android studio command line 官方文档)

TraceCompat.beginSession("sessionname"); TraceCompat.endSession(); python ~/Library/Android/sdk/platform-tools/systrace/systrace.py -b 32768 -t 5 -a com.xxx.packagename -o performance.html sched gfx view wm am app (轻量,cpu利用率直观) cpu time(优化重点指标) vs wall time, 如锁冲突

侵入式打点, 代码丑,工作量大:System.currentTimeMillis() - 执行时间, SystemClock.currentThreadTimeMillis() - cpu时间 非侵入打点,修改方便:AOP - AspectJ Joint Point, 程序执行点,方法的调用,方法的执行,获取设置变量,类初始化. Point Cut, 从Joint Point中指定条件,选出满足条件的点组成Point Cut. Advice, 插入Hook代码和插入位置(Before, After, Around) 语法:插入位置 + 切入方式(外插call, 内插execute) + 匹配规则 + 插入的代码

异步优化详解 优化技巧:Theme切换(利用用户的错觉), 子线程分担主线程任务,并行减少时间 异步优化

不满足异步要求的,需要在某阶段完成的关联任务,CPU密集型和IO密集型任务

异步初始化最优解 - 启动器1,2 常规异步的痛点:代码丑,依赖关系处理费劲,维护成本高 启动器介绍:充分利用CPU多核,自动梳理任务顺序。代码Task化,启动逻辑抽象成Task,所有任务依赖关系生成有向无环图(自动),多线程根据排序后的无环图一次执行。 启动器实践

更优秀的延迟初始化方案 常规延迟方案:挪代码,new Handler().postDelayed

  • 痛点:时机控制不够灵活,UI线程有卡顿 优秀延迟方案: 对延迟任务分批初始化且空闲时执行,IdealHandler

启动优化其他方案 优化的总方针:异步,延迟,懒加载(怎么真正改善用户体验),技术业务相结合 注意事项:wall time和cpu time, 按照systrace及cpu time跑满cpu, 监控完善性, 线上监控都阶段时间(App, Activity, Service, 生命周期间隔时间),处理聚合看趋势, 收敛启动代码的修改权限。 其他方案: 提前加载SharePreferences(MultiDex之前加载系统类,利用CPU), 重载getApplicationContext()返回this. 启动阶段不启动子进程程(共享CPU资源,导致主进程CPU紧张) 注意启动顺序, App onCreate前是ContentProvider(启动过程别启动其他组件) 类加载优化,提前异步加载类Class.fromClassName只加载类本身及其静态变量的应用类,new 类实例可以额外加载类成员变量的引用类。(通过自定义class loader打印类名观察启动时加载的类) 启动阶段抑制GC(native hook, 启动时让系统别进行GC) CPU锁频

启动优化方案总结 获取方法耗时(常规方案代码前后计时相减,AOP, wall time和cpu time区别) 异步初始化(常规异步,启动器 - 痛点及启动器优势) 延迟初始化(常规方案,结合IdleHandler) 其他方案(提前异步SharedPreferences, 启动阶段抑制子进程,提前类加载-Multidex后,CPU锁频)

Interview 你是怎么做启动优化的?

  • 分析现状,确定问题
  • 针对性优化
  • 先抽象简述,从听众感兴趣点再深入
  • 解决完问题后的长期措施 你们怎么异步,异步过程中遇到什么问题?