本文以下内容有参考下面几篇文章:

https://mp.weixin.qq.com/s/_6pulOeRmA-YVCcC2JfcLQ http://www.sohu.com/a/192682221_608959
https://www.ctolib.com/mip/topics-33344.html

冷启动启动流程:
当点击app的启动图标时,安卓系统会从Zygote进程中fork创建出一个新的进程分配给该应用,之后会依次创建和初始化Application类、创建MainActivity类、加载主题样式Theme中的        
windowBackground等属性设置给MainActivity以及配置Activity层级上的一些属性、再inflate布局、当onCreate/onStart/onResume方法都走完了后最后才进行contentView的measure/layout/draw显示在界面上,所以直到这里,
应用的第一次启动才算完成,这时候我们看到的界面也就是所说的第一帧。所以,总结一下,应用的启动流程如下:
Application的构造器方法——>attachBaseContext()——>onCreate()——>Activity的构造方法——>onCreate()——>配置主题中背景等属性——>onStart()——>onResume()——>测量布局绘制显示在界面上。

冷启动的优化:
1.在主题中加入两个属性,windowIsTranslucent和windowNoTitle,将这两个属性都设置成true,就可以让程序在初始化的时候窗口是透明的,初始化结束后程序主界面才会显示出来,从而也就完全看不到白屏界面了。(这样只能解决白屏问题,其实对冷启动并没有实质性的优化)
例如:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
</style>

2.替换第一个页面的Theme,将windowBackground设置为一张闪屏页面,然后在activity的onCreate()方法中将原来的主图设置回来(setContentView()方法前设置主题)
例如:

<style name="AppTheme.Launcher">
<item name="android:windowBackground">@drawable/window_background_statusbar_toolbar_tab</item>
</style>

(冷启动时执行了Activity的构造方法后,会先加在theme中的windowbackground等属性,然后才会inflate布局)
3.主页面布局优化(减少ui布局的层级,使用viewStub等控件;同时可以用设置中的GPU过度绘制辅助查看是否有过度绘制优化布局)
4.application和activity的OnCreate()中不要做耗时操作/或者异步初始化
5.如果主界面依赖一些数据的初始化,则可以增加一个splash的页面,在splash页面等待数据初始化完成再跳转至应用的主界面

其他一些优化方法:
1.通过 systrace 查找耗时代码,方便有针对性的修改耗时代码
2.通过 redex 重排列 class 文件

获取冷启动相关的时间:
在优化的过程中可以获取冷启动的时间做对比,可以通过一下四种方式获取时间:
第一种方式:adb命令 : adb shell am start -S -W 包名/启动类的全名
ThisTime : 最后一个 Activity 的启动耗时
TotalTime : 启动一连串的 Activity 总耗时
WaitTime : 应用进程的创建过程 + TotalTime
这里我们关注 TotalTime 就可以。
打印日志如下:(执行这个命令每次打印的时间会有一点差异)

C:\Users\Administrator>adb shell am start -S -W com.android.mms3/com.xy.bizportdemo.conversation.ConvWithViewPagerIndicatorActivity
 Stopping: com.android.mms3
 Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.android.mms3/com.xy.bizportdemo.conversation.ConvWithViewPagerIndicatorActivity }
 Status: ok
 Activity: com.android.mms3/com.xy.bizportdemo.conversation.ConvWithViewPagerIndicatorActivity
 ThisTime: 705
 TotalTime: 705
 WaitTime: 730
 Complete

第二种方式:logcat里面直接查看日志
谷歌在 Android4.4(API 19)上也提供了测量方法,在 logcat 中过滤 Displayed 字段,即可查看到对应的时间日志。
这里输出的时间是:启动进程到activity的inflate执行完成的时间,它不包含用户点击app图标然后系统开始准备启动activity的时间,这是ok的,因为作为一个开发者你无法影响这个时间,所以没有必要去测量它

例如:
10-17 11:11:33.665 1287-4614/? I/ActivityManager: Displayed com.android.mms3/com.xy.bizportdemo.conversation.ConvWithViewPagerIndicatorActivity: +705ms

第三种方式:Activity的reportFullyDrawn()方法(该方法需要UPDATE_DEVICE_STATS的权限,且该权限需要系统应用才能授权,经测试这个方法调用后日志没有打印)
前面所提到的 ‘Displayed’ 时间是自动报告的,快速测量启动初始化需要多久。但是我们通常来说会使用异步懒加载的方式来提升程序画面的显示速度,这通常会导致的一个问题是,
程序画面已经显示,可是内容却还在加载中。为了衡量这些异步加载资源所耗费的时间,我们可以在异步加载完毕之后activity.reportFullyDrawn()方法来告诉系统此时的状态,以便获取整个加载的耗时。
它将在log里报告从apk初始化(和前面Displayed的时间是一样的)到reportFullyDrawn() 方法被调用用了多长时间。
reportFullyDrawn()方法显示的log也是类似这样:
ActivityManager: Displayed com.android.myexample/.StartupTiming: +768ms

第四种:使用screenrecord命令

以上四种方式只是一个具体的时间,另外我们可以通过Method Tracing、systrace获取具体的每个方法的耗时。