性能优化---启动优化(耗时排查—代码里执行耗时还是布局耗时)

Systrace工具使用 - 简书

一:启动优化

1. 启动状态

应用有三种启动状态,冷启动、温启动与热启动;每种启动状态都会影响该应用向用户显示所需的时间。
在冷启动中,应用从头开始启动。
在另外两种状态中,系统需要将后台运行的应用带入前台。建议始终在假定冷启动的基础上进行
优化。这样做也可以提升温启动和热启动的性能。
 

冷启动

冷启动是指应用从头开始启动:系统进程在冷启动后才创建应用进程。发生冷启动的情况包括应用
自设备启动后或系统终止应用后首次启动。
 

热启动:

在热启动中,系统的所有工作就是将 Activity 带到前台。只要应用的所有 Activity 仍驻留在内存
中,应用就不必重复执行对象初始化、布局加载和绘制。
 

温启动

温启动包含了在冷启动期间发生的部分操作;同时,它的开销要比热启动高。有许多潜在状态可视
为温启动。例如:
1. 用户在退出应用后又重新启动应用。进程可能未被销毁,继续运行,但应用需要执行
onCreate() 从头开始重新创建 Activity。
2. 系统将应用从内存中释放,然后用户又重新启动它。进程和 Activity 需要重启,但传递到
onCreate() 的已保存的实例 state bundle 对于完成此任务有一定助益。
 
冷启动耗时统计
在性能测试中存在启动时间2-5-8原则:
当用户能够在2秒以内得到响应时,会感觉系统的响应很快;
当用户在2-5秒之间得到响应时,会感觉系统的响应速度还可以;
当用户在5-8秒以内得到响应时,会感觉系统的响应速度很慢,但是还可以接受;
而当用户在超过8秒后仍然无法得到响应时,会感觉系统糟透了,或者认为系统已经失去响应。
而Google 也提出一项计划: Android Vitals 。该计划旨在改善 Android 设备的稳定性和性能。当选择启用了该计 划的用户运行您的应用时,其 Android 设备会记录各种指标,包括应用稳定性、应用启动时间、电池使用情况、呈现时间和权限遭拒等方面的数据。Google Play 管理中心 会汇总这些数据,并将其显示在 Android Vitals 信息中心 内。
 
当应用启动时间过长时,Android Vitals 可以通过 Play 管理中心提醒您,从而帮助提升应用性能。Android Vitals
在您的应用出现以下情况时将其启动时间视为过长:
冷启动用了 5 秒或更长时间。
温启动用了 2 秒或更长时间。
热启动用了 1.5 秒或更长时间。实际上不同的应用因为启动时需要初始化的数据不同,启动时间自然也会不同。相同的应用也会因为在不同的设
备,因为设备性能影响启动速度不同。所以实际上启动时间并没有绝对统一的标准,我们之所以需要进行启动耗时
的统计的,可能在于产品对我们应用启动时间提出具体的要求。
 

2. 系统日志查看启动时间

在 Android 4.4(API 级别 19)及更高版本中, Andoird stutio 的logcat 会输出一行,其中包含关键字 Displayed ,该关键字的值代表 从 启动进程 到  在屏幕上完成对应 Activity 的绘制 所用的时间。

 

如果我们使用异步懒加载的方式来提升程序画面的显示速度,这通常会导致的一个问题是,程序画面已经显示,同 时 Displayed 日志已经打印,可是内容却还在加载中。为了衡量这些异步加载资源所耗费的时间,我们可以在异 步加载完毕之后调用 activity.reportFullyDrawn() 方法来让系统打印到调用此方法为止的启动耗时。
 
 

adb 命令查看启动时间

查看启动时间的另一种方式是使用命令:

 

 

输入的结果中,有如下三个参数需要关注:

1. WaitTime:总的耗时,包括前一个应用Activity pause的时间和新应用启动的时间;
2. ThisTime表示一连串启动Activity的最后一个Activity的启动耗时;
3. TotalTime表示新应用启动的耗时,包括新进程的启动和Activity的启动,但不包括前一个应用Activity pause 的耗时。
 
开发者一般只要关心 TotalTime即可,这个时间才是 自己应用真正启动的耗时
 

 

3.CPU Profifile(查看应用启动时,耗时时间花费在哪些方法上)

怎么使用 Profifile 工具???

要在应用启动过程中自动开始记录 CPU 活动,请执行以下操作:

1. 借助工具,抓trace ,查看耗时

app > Edit Confifigurations

 

各种采样的解释:

Sample Java Methods ----- java方法采样
对 Java 方法采样:在应用的 Java 代码执行期间,频繁捕获应用的调用堆栈。分析器会比较捕获的数据集, 以推导与应用的 Java 代码执行有关的时间和资源使用信息。如果应用在捕获调用堆栈后进入一个方法并在下 次捕获前退出该方法,分析器将不会记录该方法调用。如果您想要跟踪生命周期如此短的方法,应使用检测 跟踪。
 
Trace Java Methods  ------跟踪java方法
跟踪 Java 方法:在运行时检测应用,以在每个方法调用开始和结束时记录一个时间戳。系统会收集并比较这 些时间戳,以生成方法跟踪数据,包括时间信息和 CPU 使用率。
 
Sample C/C++ Functions  ------C/C++方法采样
对 C/C++ 函数采样:捕获应用的原生线程的采样跟踪数据。要使用此配置,您必须将应用部署到搭载 Android 8.0(API 级别 26)或更高版本的设备上。
 
Trace System Calls  ------跟踪系统调用
跟踪系统调用:捕获非常翔实的细节,以便您检查应用与系统资源的交互情况。您可以检查线程状态的确切 时间和持续时间、直观地查看所有内核的 CPU 瓶颈在何处,并添加要分析的自定义跟踪事件。要使用此配 置,您必须将应用部署到搭载 Android 7.0(API 级别 24)或更高版本的设备上。 此跟踪配置在 systrace 的基础上构建而成。您可以使用 systrace 命令行实用程序指定除 CPU Profifiler 提供的 选项之外的其他选项。systrace 提供的其他系统级数据可帮助您检查原生系统进程并排查丢帧或帧延迟问题。
 

选择“Trace Java Methods”,点击“Apply"后,显示的界面如下:

点击Stop,结束跟踪后显示:

四种图表的说明: Call Chart ,Flame Chart ,Top Down Tree ,Bottom Up Tree

 

Call Chart
以图形来呈现 方法/函数跟踪数据,其中调用的时间段和时间在横轴上表示,而其被调用方则在纵轴上显 示。对系统 API 的调用显示为橙色,对应用自有方法的调用显示为绿色,对第三方 API(包括 Java 语言 API)的调 用显示为蓝色。

 

如上图,自定义Application的 onCreate 调用了 Thread.sleep 耗时为:3s。
Call Chart 已经比原数据可读性高很多,但它仍然不方便发现那些运行时间很长的代码,这时我们便需要使用 Flame Chart。
 
Flame Chart
提供一个倒置的调用图表,用来汇总完全相同的调用堆栈。也就是说,将具有相同调用方顺序的完全相同的方法或 函数收集起来,并在火焰图中将它们表示为一个较长的横条 。
横轴显示的是百分比数值。由于忽略了时间线信息,Flame Chart 可以展示每次调用消耗时间占用整个记录时长的 百分比。 同时纵轴也被对调了,在顶部展示的是被调用者,底部展示的是调用者。此时的图表看起来越往上越窄, 就好像火焰一样,因此得名: 火焰图。
 
说白了就是将Call Chart上下调用栈倒过来。Call Chart 是显示在上面的横条里的方法,调用该横条下面的方法;而Flame Chart 是下面横条里的方法,会调用到该横条上面的方法。
 

 

 耗时最长的为:Thread.sleep

 

Top Down Tree
Top Down Tree显示一个调用列表

 

对于每个节点,三个时间信息:
Self Time —— 运行自己的代码所消耗的时间;
Children Time —— 调用其他方法的时间;
Total Time —— 前面两者时间之和。
 

此视图能够非常方便看到耗时最长的方法调用栈。

 

Bottom Up Tree

方便地找到某个方法的调用栈。在该列表中展开方法或函数节点会显示哪个方法调用了自己。
即展开后,是调用自己的方法。
 

 

总结:启动优化的时机是从Application的构建到主界面的onWindowFocusChanged 的这一段时间。

通过工具可以定位到耗时代码,然后查看是否可以进行优化。对于APP启动来说,启动耗时包括Android系统启动 APP进程加上APP启动界面的耗时时长,我们 可做的优化是APP启动界面的耗时,也就是说从Application的构建到主界面的 onWindowFocusChanged 的这一段时间。
 
因此在这段时间内,我们的代码需要尽量避免耗时操作, 检查的方向包括
1.主线程IO;
2.第三方库初始化或程序需要 使用的数据等初始化改为异步加载/懒加载;
3.减少布局复杂度与嵌套层级;
4.Multidex(5.0以上无需考虑)等。
 
 

2. Debug API 的方式查看耗时时间花在哪些方法上,API抓出来的文件,与Profifile抓出来的类似。

除了直接使用 Profifile 启动之外,我们还可以借助Debug API生成trace文件。
Android 8.0(API 级别 26)以下的版本,不能使用Profifile, 那就使用Debug API

运行App,则会在sdcard中生成一个enjoy.trace文件(需要sdcard读写权限)。将手机中的enjoy.trace文件保存至电 脑,随后将enjoy.trace拖入Android Studio即可, 与Profifile 抓出来的类似。
 
例如,通过抓出来的trace, 经过分析,如下地方耗时,且setChange 和 调用adapter 的刷新重复了,这样就执行了两次刷新
 

4.  StrictMode严苛模式,检测我们在主线程有没有做耗时操作 

StrictMode 是一个开发人员工具,它可以检测出我们可能无意中做的事情,比如在主线程中做了耗时操作, StrictMode提醒我们注意,以便我们能够 修复它们。
 
StrictMode最常用于捕获应用程序主线程上的磁盘或网络访问。帮助我们让磁盘和网络操作远离主线程,可以使应用程序更加平滑、响应更快。

 5. 启动黑白屏  

---- 优化点:加一个主题,把启动的Activity的主题改为想要呈现给用户的图片

当系统加载并启动 App 时,需要耗费相应的时间,这样会造成用户会感觉到当点击 App 图标时会有 “延迟” 现象,
为了解决这一问题, Google 的做法是在 App 创建的过程中, 先展示一个空白页面,让用户体会到点击图标之后立 马就有响应。
 
如果你的application或activity启动的过程太慢,导致系统的BackgroundWindow没有及时被替换,就会出现启动 时白屏或黑屏的情况(取决于Theme主题是Dark还是Light)。
消除启动时的黑/白屏问题,大部分App都 采用自己在 Theme 中设置背景图的方式来解决

 

 总结

 

启动速度优化也会涉及到布局优化与卡顿优化,包括内存抖动等问题。
优化是一条持续的道路,很多时候我们会发 现通过各种检测手段花费了大量的精力去对代码进行修改得到的优化效果可能并不理想。因为优化就是一点一滴积 累下来的,我们平时在编码的过程中就需要多注意自己的代码性能。 可能实际过程中优化并不会很顺利,不同的设备上可能表现不一样。我们只能结合对业务、对自己代码的了解去不 断去实践。
 

二. 卡顿分析

1. 怎么抓trace?

Systrace 是Android平台提供的一款工具,用于记录短期内的设备活动。
该工具会生成一份报告,其中汇总了 Android 内核中的数据,例如 CPU 调度程序、磁盘活动和应用线程。Systrace主要用来分析绘制性能方面的问题。在发生卡顿时,通过这份报告可以知道当前整个系统所处的状态,从而帮助开发者更直观的分析系统瓶颈,改进性能。
 
1). 环境要求
要使用Systrace,需要先安装 Python2.7。安装完成后配置环境变量 path ,随后在命令行输入: python -- version 进行验证。
 

 

2)cmd 命令窗口,进入到sdk/plaform-tools/systrace 目录

3)cmd窗口,进入到systrace 目录后,然后在cmd 窗口 ,可以执行如下指令

执行systrace可以选择配置 一些参数,常用的有:
gfx  ----->Graphics 图形系统,包括SerfaceFlinger,VSYNC消息,Texture,RenderThread等
input ------>Input输入系统,按键或者触摸屏输入;分析滑动卡顿等
view  ------->View绘制系统的相关信息,比如onMeasure,onLayout等;分析View绘制性能

 

例如;把这些 “gfx input view am dalvik sched wm disk res”参数全都带上去抓出,如下:

python systrace.py -t 5 -o D:\lcj\Desktop\trace\a.html gfx input view am dalvik sched wm
disk res -a com.e.demo
 
4)在抓取 systrace 文件的时候,切记不要抓取太长时间,也不要太多不同操作。

 

打开抓取的html文件,
如果只是单独存在一个红色或者黄色的都是没关系的。
如果是 连续的红/ 黄色或者 两帧间隔非常大那就需要我们去仔细观察。
“W” 放大视图,在UIThread(主线程)上面有一条很细的线,表示线程状态。
 

线程状态的学习

Systrace 会用不同的颜色来标识不同的线程状态, 在每个方法上面都会有对应的线程状态来标识目前线程所处的状态。
通过查看线程状态我们可以知道目前的瓶颈是什么, 是 CPU 执行慢还是因为 Binder 调用, 又或是进行 IO 操作, 又或是拿不到 CPU 时间片。
 

 

1. 绿色:表示正在运行;
a. 是否频率不够?(CPU处理速度)
b. 是否跑在了小核上?(不可控,但实际上很多手机都会有游戏模式,如果我们应用是手游,那系统会优 先把手游中的任务放到大核上跑。)
2. 蓝色:表示可以运行,但是 CPU 在执行其他线程;
a. 是否后台有太多的任务在跑?Runnable 状态的线程状态持续时间越长,则表示 cpu 的调度越忙,没有 及时处理到这个任务
b. 没有及时处理是因为频率太低?
3. 紫色:表示休眠,一般表示 IO

 

官方介绍为:
橙色:不可中断的休眠
线程在遇到 I/O 操作时被阻止或正在等待磁盘操作完成。
紫色:可中断的休眠
线程在遇到另一项内核操作(通常是内存管理)时被阻止。
但是实际从Android 9模拟器中拉取数据,遇到IO显示紫色,没有橙色状态显示。
 
4. 白色:表示休眠,可能是因为线程在互斥锁上被阻塞 ,如Binder堵塞/Sleep/Wait等
点击这条线的其中一种颜色,就可以看出线程的状态,如下:
 

 

2.Trace API

其实对于APP开发而言,使用Systrace的帮助并不算非常大,大部分内容用于设备真机优化之类的系统开发人员观察。Systrace无法帮助应用开发者定位到准确的错误代码位置,我们需要凭借很多零碎的知识点与经验来猜测问题原因。
 
如果我们有了 大概怀疑的具体的代码块或者有 想了解的代码块执行时系统的状态,还可以结合 Trace API 打 标签。
Android 提供了Trace API能够帮助我们记录收集自己应用中的一些信息 : Trace.beginSection() 与 Trace.endSection();

 

3. android studio 抓trace 和分析trace

怎么抓?

第一步是:添加进程

第二步是:选中分析的模块是CPU

第三步是:选中Trace system calls

第四步是:点击“record" 开机记录/收集 的按钮,然后去手机上操作自己的应用

第五步是:在手机上操作完成后,再在点击”stop"按钮

怎么分析?

点击“stop"停止收集trace后,studio 界面会显示每一帧的情况,主要看Frames ,Frames也就是帧,拿耗时多的分析

4. 指令抓trace 以及分析

怎么抓?

1. 进入到sdk/plaform-tools/systrace 目录下,输入”cmd"

2. 输入指令后,回车,看到开始收集“starting tracing(5 seconds)",这个时候,操作一下自己的应用

3. 收集完成后,会在指定的目录生成一个trace 文件

怎么分析?

 

App 层面监控卡顿
systrace可以让我们了解应用所处的状态,了解应用因为什么原因导致的。若需要准确分析卡顿发生在什么函数,
资源占用情况如何,目前业界两种主流有效的app监控方式如下:
1、 利用UI线程的Looper打印的日志匹配;
2、 使用Choreographer.FrameCallback
 
Looper  日志检测卡顿
Android主线程更新UI。如果界面1秒钟刷新少于60次,即FPS小于60,用户就会产生卡顿感觉。简单来说, Android使用消息机制进行UI更新,UI线程有个Looper,在其loop方法中会不断取出message,调用其绑定的Handler在UI线程执行。如果在handler的dispatchMesaage方法里有耗时操作,就会发生卡顿。
只要检测 msg.target.dispatchMessage(msg) 的执行时间,就能检测到部分UI线程是否有耗时的操作。注意到这行执行代码的前后,有两个logging.println函数,如果设置了logging,会分别打印出 >>>>> Dispatching to<<<<< Finished to 这样的日志,这样我们就可以通过两次log的时间差值,来计算dispatchMessage的执行时间,从而设置阈值判断是否发生了卡顿。
由上图可知:Looper 提供了 setMessageLogging(@Nullable Printer printer) 方法,所以我们可以自己实现一个Printer,在通过setMessageLogging()方法传入即可。

 

 

其实这种方式也就是 BlockCanary 原理。

 三. 布局优化

Layout Inspector来检查应用的视图层次结构

 

 

使用merge标签-------减少布局层次

当我们有一些布局元素需要被多处使用时,这时候我们会考虑将其抽取成一个单独的布局文件。在需要使用的地方 通过 include 加载。
当include 的布局,与外层的布局都是一样,例如都是LinearLayout 时,include的布局的LinearLayout就没有意义了。

 

 

使用ViewStub 标签 -----需要时显示,不需要时不显示且不占用资源

当我们布局中存在一个View或者ViewGroup,在某个特定时刻才需要他的展示时,可能会有同学把这个元素在xml中 定义为invisible或者gone,在需要显示时再设置为visible可见。比如在登陆时,如果密码错误在密码输入框上显示提示。
 
invisible -----会占用位置,会初始化,会占用资源
view设置为invisible时,view在layout布局文件中会占用位置,但是view为不可见,该view还是会创建对象,会被初始化,会占用资源。
gone ------ 不会占位置,会初始化,会占用资源
view设置gone时,view在layout布局文件中不占用位置,但是该view还是会创建对象,会被初始化,会占用资源。
 
如果view不一定会显示,此时可以使用 ViewStub 来包裹此View, 
viewstub是一个轻量级的view,它不可见,不用占用资源,只有设置viewstub为visible或者调用其inflflater()方法 时,其对应的布局文件才会被初始化。
 
加载viewStub后,可以通过 inflatedId 找到layout_viewstub 中的根View。
 
 

过度渲染

过度绘制是指系统在渲染单个帧的过程中多次在屏幕上绘制某一个像素。例如,如果我们有若干界面卡片堆叠在一起,每张卡片都会遮盖其下面一张卡片的部分内容。但是,系统仍然需要绘制堆叠中的卡片被遮盖的部分。
GPU 过度绘制检查
手机开发者选项中能够显示过度渲染检查功能,通过对界面进行彩色编码来帮我们识别过度绘制。开启步骤如下:
1. 进入 开发者选项 (Developer Options)。
2. 找到 调试 GPU 过度绘制(Debug GPU overdraw)。
3. 在弹出的对话框中,选择 显示过度绘制区域(Show overdraw areas)。
   

 解决过度绘制问题

可以采取以下几种策略来减少甚至消除过度绘制:
移除布局中不需要的背景。
默认情况下,布局没有背景,这表示布局本身不会直接渲染任何内容。但是,当布局具有背景时,其有 可能会导致过度绘制。
 
移除不必要的背景可以快速提高渲染性能。不必要的背景可能永远不可见,因为它会被应用在该视图上绘制的任何其他内容完全覆盖。例如,当系统在父视图上绘制子视图时,可能会完全覆盖父视图的背景。
 
使视图层次结构扁平化。
可以通过优化视图层次结构来减少重叠界面对象的数量,从而提高性能。
 
降低透明度。
对于不透明的 view ,只需要渲染一次即可把它显示出来。但是如果这个 view 设置了 alpha 值,则至少需要渲染两次。这是因为使用了 alpha 的 view 需要先知道混合 view 的下一层元素是什么,然后再结合上层的 view 进行Blend混色处理。透明动画、淡入淡出和阴影等效果都涉及到某种透明度,这就会造成了过度绘制。可以通过减少要渲染的透明对象的数量,来改善这些情况下的过度绘制。例如,如需获得灰色文本,可以在 TextView 中绘制黑色文本,再为其设置半透明的透明度值。但是,简单地通过用灰色绘制文本也能获得同样的效果,而且能够大幅提升性能。
 
布局加载优化
异步加载
LayoutInflflater加载xml布局的过程会在主线程使用IO读取XML布局文件进行XML解析,再根据解析结果利用反射 创建布局中的View/ViewGroup对象。这个过程随着布局的复杂度上升,耗时自然也会随之增大。Android为我们提供了 Asynclayoutinflater 把耗时的加载操作在异步线程中完成,最后把加载结果再回调给主线程。

1、使用异步 inflflate,那么需要这个 layout 的 parent 的 generateLayoutParams 函数是线程安全的;
2、所有构建的 View 中必须不能创建 Handler 或者是调用 Looper.myLooper;(因为是在异步线程中加载的,异
步线程默认没有调用 Looper.prepare );
3、AsyncLayoutInflflater 不支持设置 LayoutInflflater.Factory 或者 LayoutInflflater.Factory2;
4、不支持加载包含 Fragment 的 layout
5、如果 AsyncLayoutInflflater 失败,那么会自动回退到UI线程来加载布局。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值