查看启动时间的另一种方式是使用命令:
输入的结果中,有如下三个参数需要关注:

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
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线程来加载布局。