APP冷启动优化:如何使用好工具【Perfetto\ systrace \MethodTracing\】
APP的性能提升无非就是围绕稳定、流畅之类的指标做文章,在推动性能提升的时候,什么才是关键,热情?能力 ?规范?,个人认为是工具,用好性能分析工具,性能提升就走完了一大半,就好比:”算数我比不过小王,但我找了个电子计算器“。以提升冷启动速度为例,看看整体的性能优化流程应该是什么样子,而在这其中性能工具能带来什么。
冷启动的定义与可优化的点
如何衡量当前的性能指标,个人感觉,性能的衡量分三步: 指标制-> 指标采集 -> 性能基线与优劣评级, 以上三块组成性能量化工具,有了量化工具,就可以说APP性能是好是坏,以冷启动为例,冷启动指标如何制定?单从技术上说感觉可以定义如下:
冷启动耗时 = 从APP进程创建到第一个有效页面帧[闪屏]
具体到实现上,涉及哪些环节,会怎样影响冷启动速度呢?
冷启动->系统会启动一个StartWindow占位-> 启动进程->创建Application->Application中初始化全局配置->启动第一个Activity->Create->Start->Resume->AddWindow->UI测量绘制[performTraversals]->首帧可见
冷启动的时候,系统一般会先启动一个占位Window,默认是个白屏窗口,复用的是第一个启动Activity配置,在体感上,主要下面的Activity配置
<item name="android:windowBackground">@drawable/xxx</item>
它一般是SplashActivity的配置,用品牌图做个中转,这个图最好要限制下尺寸,否则在解析上影响启动速度。随后系统会启动进程加载SplashActivity,启动进程主要是Application中可能有些APP全局初始化操作,尽量轻,或者延后处理,当然,也会有一些ContentProvider与Receiver影响启动这些都可以通过工具查看。
public class LabApplication extends Application {@Overridepublic void onCreate() {super.onCreate()<!--UI中不要处理耗时操作-->}
之后便是Activity的创建与启动流程 :
可以看到上图的Activity启动流程都是被动消息的处理,主要是受控AMS指挥,代码中设置View及显示的流程也就上图的几个点,比如onCreate中设置Layout并inflater,当然,这不是必须的,即使不主动setContentView,在后面的wm.addView中也会创建顶层DecorView。
@Override
public void setContentView(int resId) {ensureSubDecor();ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);contentParent.removeAllViews();<!--可能影响耗时-->LayoutInflater.from(mContext).inflate(resId, contentParent);mAppCompatWindowCallback.getWrapped().onContentChanged();
}
而setContentView的inflate可能是影响耗时的一个点。之后handleResumeActivity中,会想WMS添加窗口View
@Overridepublic void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,String reason) {...final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);...r.window = r.activity.getWindow();View decor = r.window.getDecorView();if (!a.mWindowAdded) {a.mWindowAdded = true;<!--添加Window-->wm.addView(decor, l);} else {@Overridepublic final View getDecorView() {if (mDecor == null || mForceDecorInstall) {installDecor();}return mDecor;}
可以看到 getDecorView会兜底处理Activity 的顶层窗口创建逻辑。addView会调用WindowManagerGlobal的addView,进而创建ViewRootImpl,利用ViewRootImpl进一步添加Window
public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {<!--关键-->root = new ViewRootImpl(view.getContext(), display);view.setLayoutParams(wparams);<!--添加到WMS,处理UI显示-->root.setView(view, wparams, panelParentView);}}
而ViewRootImpl接管流程之后,所以View相关的操作都将在ViewRootImpl处理,而最终由其requestLayout触发测量、布局、绘制的动作
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {synchronized (this) {if (mView == null) {<!--申请下个Message绘制-->requestLayout();<!--添加窗口--> mOrigWindowType = mWindowAttributes.type;mAttachInfo.mRecomputeGlobalAttributes = true;collectViewAttributes();res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);}}
}
requestLayout会scheduleTraversals预先占位一个异步消息,用于接收并doScheduleCallback触发的VSYNC信号,这样可以保证之后插入的消息都被延期处理,从而Window被添加后,UI绘制任务第一时间执行。
void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;<!--异步消息-->mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();<!--插入绘制请求,触发VSYNC-->mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);}
等待VSYNC消息回来后,撤离异步消息栅栏,第一时间处理UI绘制:
所以首帧的渲染一定是在Resume之后,那么具体的时机怎么把控?到底在哪,如下图所示,插入的点在哪?
网上有一些其他的实现,认为可以监听onAttachedToWindow或者OnWindowFocusChange,onAttachedToWindow的问题是可能太过靠前,还没有Draw, OnWindowFocusChange的缺点可能是太过滞后,其实可以简单认为view会的draw以后,View的绘制就算完成,虽然到展示还可能相差一个VSYNC等待图层合成,但是对于性能监测的评定,误差一个固定值可以接受:
在onResume函数中插入一条消息可以吗,理论上来说,太过靠前,这条消息在执行的时候,还没Draw,因为请求VSYNC的同步栅栏是在是在Onresume结束后才插入的,无法拦截之前的Message,但是由于VSYNC可能存在复用,Onresume中插入的消息也有可能会在绘制之后执行,这个不是完全一定的,比如点击MaterialButton启动一个Activity,第二个Activity的setView触发的VSYNC就可能复用MaterialButton的波纹触发的VSYNC,从而导致第二个Activity的performTraval复用第一个VSYNC执行,从而发生在onResume插入消息之前,如下
栅栏消息
重绘CallBack包含多个Activity的重绘
综上所述,将指标定义在第一次View的Draw执行可能比较靠谱。具体可以再DecorView上插入一个透明View,监听器onDraw回调即可,如果觉得不够优雅,就退一步,监听OnWindowFocusChange的回调,也勉强可以接受, OnWindowFocusChange一定是在Draw之后的。有了指标,那是否达标?如何采集?基线呢?可以参考业界做法,采集方式可以无入侵打点,而优秀基线可以认为:
优秀=秒开
如果发现不达标,接下来要做的就是定位+优化,这个时候就体现分析工具的重要性,其实上述的原理分析就已经借助Studio自带的Profiler工具,在理解流程上事半功倍。
如何定位当前性能问题
冷启动每个阶段的耗时可以通过多种工具、方式来定位:可以用的有Debug.startMethodTracing跟踪,也可以利用perfetto/systrace来查看,甚至还可以用Studio自身的Profiler跟踪,每种方式都有自己的优势,可配合选择使用。
Debug.startMethodTracing 适合查看UI线程的耗时函数
Debug.startMethodTracing是通过应用插桩来生成跟踪日志,做到对方法的跟踪。但是启用剖析功能后,应用的运行速度会减慢,所以,不应使用剖析数据确定绝对时间,最大的作用是用在对比上,可以对比之前,或者对比周围函数。具体用法:
private void startTrace() {File file = new File(getApplication().getExternalFilesDir("android"), "methods.trace");Debug.startMethodTracing(file.getAbsolutePath(), 100 * 1024 * 1024);
}
<!-注意配对使用-->
private void stopTrace() {Debug.stopMethodTracing();
}
对于冷启动:进程启动时开启监听,在合适节点配对停止即可,之后导出.trace文件在Studio中分析,可以看到关键函数耗,Studio提供了多种模式,Flame Chart、Top Down、Bottom Up、Event,不同的模式侧重点不同。定向分析的时候,可以分段锁定范围,比如冷启动可分几个阶段排查,进程创建、Application初始化、Activity的创建、create、resume、draw等,先选定Main线程,然后将范围限制定Application阶段,如下下:
- Flame Chart:更侧重直观反映函数耗时严重程度
比如上图,浅黄色部分其实就是需要重点关注的部分,耗时最多的函数,会最先展示,更加方便定位严重问题,大致定位问题后,就可以用Top Down 进一步看细节。
- Top Down: 更侧重自顶向下详细排查
利用Top-Down模式可以更精确观察函数耗时与调用堆栈,更加清晰,如下在Application初始化阶段,可以清醒看到函数调用顺序、耗时等,
对于冷启动,重点排查耗时函数,尝试将非核心逻辑从UI线程中移除。同理对于闪屏Activity的onCreate跟onResume阶段所做的处理类似
从图中就很容下发现,有些Flutterboost、埋点Json解析类的耗时操作被不小心关联进了Activit的启动流程中,拖慢了冷启动速度,那就可以放到非UI线程中处理,或者延后处理。
- Bottom Up:一种平铺的模式,
这个模式个人用的不多,罗列的函数太多,没有层次,可能单独看排名靠前的几个有些收益。
依赖profiler基本能定位哪些函数导致了冷启动速度慢,但是这些函数可能并非自己耗时严重,也许是会因为调度或者锁的原因导致慢,这个时候perfetto/systrace会提供更多帮助。
perfetto/systrace:大局与调度
perfetto地址及使用文档
perfetto/systrace是官方提供另一种性能分析工具,其中perfetto可以看做是systrace的升级版。相比MethodTracing代码插桩,无法具体到每个方法,但可以提供全局性能概览,可以更快定位问题范围,而且perfetto/systrace在全局任务调度、系统调用上更具优势,MethodTracing多少对于性能有些影响,而perfetto/systrace借助系统本身lOG,可以降低自身带来的影响,用perfetto看一下冷启动的流程,如下:
如图,首先你就能直观的看到那些阶段的耗时比较严重,然后定向分析即可,将时间段收缩,放大观察:
可以直观看出Activity启动时蓝色标记的资源解析耗时过长,定向排查后发现图大
Name res/BKC.xml
Category null
Start time 1s 309ms 568us 459ns
Duration 42ms 35us 682ns
Slice ID 2465
适当将图缩小,降低加载成本,经过优化缩短到8ms
Name res/BKC.xml
Category null
Start time 964ms 602us 749ns
Duration 8ms 260us 261ns
Slice ID 11851
type internal_slice
再比如,对于有些阶段,UI线程莫名的睡眠,其实可以比较方便的查看是什么因素导致的,如下:
如上所示,在当前阶段,UI线程因为没有获取锁进入了睡眠,之后,被另一个线程唤起了,这就可以排查到底是哪个地方有问题,是否可以避免,大概的使用方式就是如此。
对于整体冷启动优化效果:用perfetto看比较直接
优化前:1261ms
优化后:439ms
总结
BUG是必然的,优化是持久的,如何用好工具是关键的。
APP冷启动优化:如何使用好工具【Perfetto\ systrace \MethodTracing\】相关推荐
- 从细节处谈Android冷启动优化
本文来自网易云社区 Android APP冷启动优化,对于Android开发同学而言可能是个老生常谈的技优了. 之所以花时间写一篇冷启动优化的文章: 我想从另外一个角度来说冷启动优化,如题所述,从细节 ...
- app冷启动与热启动原理,及启动优化
-- app热启动 app热启动: 当应用已经被打开, 但是被按下返回键.Home键等按键时回到桌面或者是其他程序的时候,再重新打开该app时, 这个方式叫做热启动(后台已经存在该应用进程).热启 ...
- App性能优化(一)—— 启动优化,冷启动,热启动,温启动
标签 : Android架构师之路 APP启动方式 App启动方式分三种:冷启动(cold start).热启动(hot start).温启动(warm start) ▲ 冷启动 系统不存在App进程 ...
- Android 系统性能优化(72)-----App启动优化
App启动优化的一篇深度好文 原文地址: http://www.jianshu.com/p/c056e63dc7a2 正文 对于Android平台上的线程优先级设置来说可以处理很多并发线程的阻塞问题, ...
- Android 性能优化---(8)APP启动时间优化指南
本文可以帮助你优化应用的启动时间:首先描述应用启动过程的内部机制:然后讨论如何分析启动性能:最后,列举了一些常见的影响启动时间的问题,并就如何解决这些问题给出一些提示. 第 1 部分:启动过程内部机制 ...
- App性能优化(布局优化,线程优化,app瘦身优化,页面切换优化,App启动优化,内存优化)
Android APP性能优化(最新总结) 在目前Android开发中,UI布局可以说是每个App使用频率很高的,随着UI越来越多,布局的重复性.复杂度也随之增长,这样使得UI布局的优化,显得至关重要 ...
- 友盟+U-APM:全方面无忧助力APP启动速度优化
首先,我们来说一说影响移动APP启动速度的原理以及因素有哪些. 要想了解APP的启动,需要先了解APP的基本启动方式. APP启动方式粗略来看有三种:冷启动(cold start).热启动(hot s ...
- [纸上谈兵 1]——App启动优化
0 纸上谈兵--App启动优化 纸上谈兵系列是我在学习App性能优化的笔记,纸上谈兵这个名字就很好的反应了这次只是启动优化的学习,并没有真正用到实际App的开发过程中(以后专门的同时处理),闲话少说, ...
- Bilibili 移动端组件化实践中的冷启动优化
编者按:移动互联网的火热改变着人们的生活方式,从移动社交到移动购物再到如今迅猛发展的互娱行业.移动端的力量不可忽视,Bilibili 的移动端项目目前已经进行了接近一年多的组件化实践,其中部分方案是基 ...
最新文章
- django html菜单,django实现动态菜单的方式
- 忘记MySQL密码怎么办
- eclipse生成java项目出错,Java项目使用了HttpClients相关包,用eclipse导出jar包就不能正常运行Error: A JNI error has occurred...
- Pytorch Merge操作
- CSP认证201403-3	命令行选项[C++题解]:模拟题、字符串处理、stringstream处理getline
- zoj2968 Difference Game
- java Spring Boot中使用Swagger2构建API文档
- 学会了这些技术,你离BAT大厂不远了
- 实战课堂:数据库高Library Cache Lock导致Hang的故障分析
- iOS项目中的网络请求和上下拉刷新封装
- 经典的0.001秒,让程序回复正常的0.001秒。
- Ubuntu Server最佳方案——LAMP服务器之PHP篇
- [转]20款Notepad++插件下载和介绍
- xlswrite wps matlab,Matlab实例学习-----Matlab与Excel交互,Matlab作为自动化客户端调用Excel服务器...
- python -m a.py 和 python a.py区别
- 白鹭引擎拉伸高度_摩托车界厚道王!129cc单缸风冷引擎,座高740mm+数字盘,6400起...
- createdroptargets_拖拽神器React DnD你真的了解了吗?
- 按键精灵post教程_安卓按键精灵教程VIP按键精灵辅助post视频教程大漠插件编程实战...
- php仿it之家源码,织梦仿IT之家带wap手机版 v5.7
- 深度学习:卷积神经网络(详解版)
热门文章
- 跨网络加密软件部署办法--动态域名NAT
- python 通讯录系统_Python实现通讯录功能
- 盘点七大接地气的翅片管式换热器设计软件
- 一种改进的人工鱼群算法及其应用(Matlab代码实现)
- matlab plot画曲线/直线/椭圆
- 群晖搭建服务器(外网访问+开机自启动)
- java程序设计第四版_Java 程序设计语言(第4版) PDF扫描版[29MB]
- A Survey on VQA: Datasets and Approaches
- 用java实现基于感知器的数据线性分类
- Windows10基于chrome内核的浏览器不走代理解决方法