16.App启动优化实践
目录介绍
- 01.启动优化背景
- 1.1 项目背景介绍
- 1.2 概念说明
- 1.3 衡量标准
- 1.4 建设目标
- 1.5 产生收益分析
- 02.启动流程分析
- 2.1 App启动流程
- 2.2 什么是冷启动
- 2.3 什么是热启动
- 2.4 什么是温启动
- 2.5 启动流程优化点
- 2.6 冷启动时间检测
- 2.7 MultiDex耗时
- 03.启动优化思路分析
- 3.1 优化如何着手
- 3.2 常见优化策略
- 3.3 业务梳理优化思路
- 3.4 任务调度优化
- 3.5 修改消息队列
- 04.启动优化实践操作
- 4.1 启动阶段代码梳理
- 4.2 收集耗时信息
- 4.4 任务调度实践
- 4.5 并发控制顺序
- 4.7 App启动白屏优化
- 05.方案基础设计
- 5.1 整体架构图
- 5.2 UML设计图
- 5.3 关键流程图
- 5.4 接口设计图
- 5.5 模块间依赖关系
- 06.其他设计说明
- 6.1 性能设计
- 6.2 稳定性设计
- 6.3 灰度设计
- 6.4 降级设计
- 6.5 异常设计
- 6.6 兼容性设计
- 6.7 自测case设计
- 07.启动优化总结
- 7.1 启动优化怎么做
- 7.2 如何异步策略
- 7.3 忽略的注意点
- 7.4 迭代优化方案
- 7.5 启动效果分析
01.启动优化背景
1.1 项目背景介绍
用户希望应用能够及时响应并快速加载。启动时间过长的应用不能满足这个期望。提高程序的启动速度意义重大,很显然,启动时间越短,会让用户感觉使用越流畅!
1.2 概念说明
Android启动优化是指通过各种技术和策略来减少应用程序的启动时间和提高用户体验的过程。启动时间是指从用户点击应用程序图标到应用程序完全可用的时间。
1.3 衡量标准
在Android应用程序的启动优化中,有几个常见的衡量标准可以用来评估应用程序的启动性能。以下是一些常见的启动优化衡量标准:
冷启动时间(Cold Start Time):冷启动是指应用程序从完全关闭状态启动的情况。冷启动时间是指从用户点击应用程序图标到应用程序完全可用的时间。较短的冷启动时间表示应用程序能够快速响应用户的操作。
热启动时间(Warm Start Time):热启动是指应用程序在后台运行或被系统保留在内存中,再次启动的情况。热启动时间是指从用户点击应用程序图标到应用程序完全可用的时间。较短的热启动时间表示应用程序能够快速恢复到之前的状态。
内存占用(Memory Footprint):应用程序的内存占用是指应用程序在运行时所使用的内存量。较低的内存占用可以减少系统的内存压力,提高应用程序的响应速度和性能。
启动时的CPU使用率(CPU Usage at Startup):启动时的CPU使用率是指应用程序在启动过程中所使用的CPU资源。较低的CPU使用率表示应用程序在启动时对系统资源的需求较低,可以更快地启动和响应用户操作。
冷启动和热启动的可视反馈时间(Visual Feedback Time):可视反馈时间是指应用程序在启动过程中向用户提供可见的UI反馈的时间。较短的可视反馈时间可以给用户更好的体验,让他们感觉应用程序在启动时更加快速和响应。
1.4 建设目标
在进行Android启动优化时,可以设定以下建设目标:
减少启动时间:主要目标是缩短应用程序的冷启动时间和热启动时间,通过优化资源加载、初始化操作和代码执行,减少启动时间。
提高用户体验:通过减少启动时间、显示启动画面、提供即时的反馈等方式,可以让用户感觉应用程序更快、更流畅。
优化启动流程:通过分析应用程序的启动流程。例如,可以将初始化操作分解为多个异步任务,优化资源加载顺序等。
保持稳定性:通过测试和优化,确保应用程序在各种情况下都能正常启动和运行。
02.启动流程分析
2.1 App启动流程
App启动类型有哪些么?有冷启动,热启动,温启动。接下来,一起来了解下它们的启动流程和区别……
2.2 什么是冷启动
什么是冷启动 - 场景:开机后第一次启动应用 或者 应用被杀死后再次启动生命周期。冷启动是最慢最耗时的,系统以及应用本身都有大量的工作需要处理,所以,冷启动对于应用的启动速度是最具挑战以及最有必要进行优化的。 - 特点:冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化application类,再创建和初始化Activity类(包括一系列的测量、布局、绘制),最后显示在界面上。
- 冷启动大概流程
- Process.start->Application创建->attachBaseContext->onCreate->onStart->onResume->Activity生命周期启动速度:主要是优化这块的启动过程时间。
- 在冷启动的最开始,系统需要负责做三件事:
- 1.加载以及启动app;2.app启动之后立刻显示一个空白的预览窗口;3.创建app进程
2.3 什么是热启动
- 什么是热启动
- 场景:Home键最小化应用生命周期,然后从后台切换到前台操作
- onResume->Activity生命周期启动速度:快
- 详细一点说
- 热启动:当启动应用时,后台已有该应用的进程(例:按back键、home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热启动。
- 特点:热启动因为会从已有的进程中来启动,所以热启动就不会走application这步了,而是直接走MainActivity(包括一系列的测量、布局、绘制),所以热启动的过程只需要创建和初始化一个MainActivity就行了,而不必创建和初始化application,因为一个应用从新进程的创建到进程的销毁,application只会初始化一次。
2.4 什么是温启动
- 什么是温启动
- 场景:应用已经启动,返回键退出生命周期:onCreate->onStart->onResume->Activity生命周期启动速度:较快
2.5 启动流程优化点
- 通过翻阅 Application启动的源码
- 当我们点击桌面图标进入我们软件应用的时候,会由 AMS 通过 Socket 给 Zygote 发送一个 fork 子进程的消息,当 Zygote fork 子进程完成之后会通过反射启动 ActivityThread##main 函数,最后又由 AMS 通过 aidl 告诉 ActivityThread##H 来反射启动创建Application 实例,并且依次执行 attachBaseContext 、onCreate 生命周期,由此可见我们不能在这 2 个生命周期里做主线程耗时操作。
- 应用进程不存在的情况下,从点击桌面应用图标,到应用启动(冷启动),大概会经历以下流程:
- 1.Launcher startActivity
- 2.AMS startActivity
- 3.Zygote fork 进程
- 4.ActivityThread main()
- 4.1 ActivityThread attach
- 4.2 handleBindApplication
- 4.3 attachBaseContext
- 4.4 installContentProviders
- 4.5 Application onCreate
- 5.ActivityThread 进入loop循环
- 6.Activity生命周期回调,onCreate、onStart、onResume...这个主要是第一个页面的页面加载,渲染到展现
- 整个启动流程我们能干预的主要是4.3、4.5和6,应用启动优化主要从这三个地方入手。
- 理想状况下,这三个地方如果不做任何耗时操作,那么应用启动速度就是最快的,但是现实很骨感,很多开源库接入第一步一般都是在Application onCreate方法初始化,有的甚至直接内置ContentProvider,直接在ContentProvider中初始化框架,不给你优化的机会。
2.6 冷启动时间检测
- 要想提升APP启动时的性能,加快启动速度,首先我们应该正确的获取和分析应用的启动时间和性能。为了正确诊断应用的启动时间和性能,我们可以跟踪一些显示应用启动所需时间的指标。
2.6.1 adb获取冷启动时间
- 可以通过adb命令,使用am服务来获取冷启动时间。指令的含义: -S表示每次启动前先强行停止;-R表示重复测试次数。
adb shell am start -S -R 10 -W com.yc.ycandroidtool/包名.MainActivity adb shell am start -W [packageName]/[MainActivity路径]
- 结果显示: TotalTime:应用的启动时间,包括创建进程+Application初始化+Activity初始化到界面显示;WaitTime:一般比TotalTime大点,是AMS启动Activity的总耗时。
2.6.2 通过logcat获取
- 在 Android 4.4(API 级别 19)及更高版本中,logcat 包含一个输出行,其中包含名为 Displayed 的值。
- 此值代表从启动进程到在屏幕上完成对应 Activity 的绘制所用的时间。这个方法比较适合测量程序的启动时间。报告的日志行类似于以下示例:
ActivityManager: Displayed com.yc.ycandroidtool/.StartupTiming: +3s534ms
- 以上时间包含以下事件序列:启动进程。初始化对象。创建并初始化 Activity。扩充布局。首次绘制应用。
- 这个信息在 Activity 窗口完成所有的启动事件之后,第一次绘制的时候输出。这个时间包括了从启动进程到第一次布局与绘制的所有时间。这基本上是你需要知道的主要时间。它不包含用户点击app图标然后系统开始准备启动activity的时间,这是ok的,因为作为一个开发者你无法影响这个时间,所以没有必要去测量它。
2.6.3 代码方式埋点
- 代码方式埋点如何做
- 通过代码埋点来准确获取记录每个方法的执行时间,知道哪些地方耗时,然后再有针对性地优化。例如通过在 app 启动生命周期中,关键位置加入时间点记录,达到测量目的。
- 问题
- 从用户点击 app Icon 到 Application 被创建,再到 Activity 的渲染,中间还是有很多步骤的,比如冷启动的进程创建过程,而这个时间用此版本是没办法统计了,必须得承受这点数据的不准确性。
2.6.4 其他方式有哪些
- TraceView
- 通过 TraceView 主要可以得到两种数据:单次执行耗时的方法 以及 执行次数多的方法。但是TraceView 性能耗损太大,不能比较正确反映真实情况。
- Systrace
- Systrace 能够追踪关键系统调用的耗时情况,如系统的 IO 操作、内核工作队列、CPU 负载、Surface 渲染、GC 事件以及 Android 各个子系统的运行状况等。但是不支持应用程序代码的耗时分析。
2.6.5 获取完全显示时间
- 通常来说会使用异步懒加载的方式来提升程序画面的显示速度
- 这通常会导致的一个问题是,程序画面已经显示,可是内容却还在加载中。为了衡量这些异步加载资源所耗费的时间,我们可以在异步加载完毕之后调用 activity.reportFullyDrawn() 方法来告诉系统此时的状态,以便获取整个加载的耗时。在应用执行延迟加载时,此数据会很有用。
- 获取时间可能不太准
- 如果由于延迟加载,应用的初步显示不包括所有资源,我们可能需要将完全加载和显示所有资源及视图视为单独的指标:例如,你的界面可能已完全加载,并绘制了一些文本,但尚未显示应用必须从网络中获取的图片。
- 要解决此问题,你可以手动调用 reportFullyDrawn(),让系统知道你的 Activity 已完成延迟加载。
- 当你使用此方法时,logcat 显示的值为从创建应用对象到调用 reportFullyDrawn() 时所用的时间。
- 获取完全显示时间有哪些方式
- 待完善
2.7 MultiDex耗时
- 在apk编译流程中,将class文件转换成dex文件,默认只会生成一个dex文件,单个dex文件中的方法数不能超过65536,不然编译会报错。
- 整个App的方法数一般都是超过65536的,解决办法就是:一个dex不够用,那就多来几个dex,gradle增加一行配置即可(multiDexEnabled true)。
- ClassLoader如何从dex1,dex2,dex3中找到对应的类?
03.启动优化思路分析
3.1 优化如何着手
- 提高app的启动速度,加快Application的执行时间也是一个很重要的方面,这里我暂时总结了几条原则:
- 尽量不将一些业务逻辑放于Application中;
- Application尽量不以静态变量的方式保存应用数据;
- 若App的大小不是特别大无需使用dex分包方案;
- 在Application中关于文件,数据库等耗时的操作尽量放到IntentService线程中处理
- 不要做有关于循环的操作
- 常见的启动优化方式大概有这些:
- MultipDex优化
- 第三方库懒加载
- WebView优化
- 线程优化
- 系统调用优化
- 启动页面屏蔽返回按键
- 一般App中都会在启动页面执行一些初始化配置等,所以这时候启动页加载时不希望用户通过按下返回按键退出App,因而可以在启动页中屏蔽返回按键
- 知道了 attachBaseContext 、onCreate 在应用中最先启动,那么我们就可以通过 TraceView 等性能检测工具,来检测具体函数耗时时间,然后来对其做具体的优化。
- 1.项目不及时需要的代码通过异步加载。
- 2.将对一些使用率不高的初始化,做懒加载。
- 3.将对一些耗时任务通过开启一个 IntentService来处理。
- 4.还通过 redex 重排列 class 文件,将启动阶段需要用到的文件在 APK 文件中排布在一起,尽可能的利用 Linux 文件系统的 pagecache 机制,用最少的磁盘 IO 次数,读取尽可能多的启动阶段需要的文件,减少 IO 开销,从而达到提升启动性能的目的。
- 5.通过抖音发布的文章知晓在 5.0 低版本可以做 MultiDex 优化,在第一次启动的时候,直接加载没有经过 OPT 优化的原始 DEX,先使得 APP 能够正常启动。然后在后台启动一个单独进程,慢慢地做完 DEX 的 OPT 工作,尽可能避免影响到前台 APP 的正常使用。
3.2 常见优化策略
Android启动优化概念和技术:
延迟加载:延迟加载是指将应用程序的某些资源或功能推迟到需要时再加载。通过延迟加载不必要的资源,可以减少启动时间和内存占用。
异步初始化:将应用程序的初始化操作分解为多个异步任务,以便在启动过程中并行执行。这样可以提高启动速度,因为不同的初始化任务可以同时进行。
代码优化:通过优化应用程序的代码,例如减少不必要的计算、避免过度绘制、优化布局等,可以提高应用程序的启动速度和响应性能。
启动画面(Splash Screen):使用启动画面可以在应用程序启动时显示一个占位符界面,以提供即时的反馈给用户,同时后台进行必要的初始化操作。
3.3 业务梳理优化思路
- 不要一股脑把全部初始化工作放在 Application 中做。
- 需要梳理清楚当前启动过程正在运行的每一个模块,哪些是一定需要的、哪些可以砍掉、哪些可以懒加载。
- 但是需要注意的是,懒加载要防止集中化,否则容易出现首页显示后用户无法操作的情形。
- 总的来说,用以下四个维度分整理启动的各个点:
- 必要且耗时:启动初始化,考虑用线程来初始化。
- 必要不耗时:首页绘制。
- 非必要但耗时:数据上报、插件初始化。
- 非必要不耗时:不用想,这块直接去掉,在需要用的时再加载。
- 优化思路
- 按需实现加载逻辑,采取分步加载、异步加载、延期加载策略
- 分布加载:以大化小,优先级高的放到前面
- 异步加载:耗时多的可以异步话
- 延期加载:非必要的数据可以延期加载
3.4 任务调度优化
- 把主线程的串行任务变成并发任务
- 提起并发,在Android最常用的基本就是AsyncTask和线程池,把所有任务都放在子线程里去调度是否真的会减少冷启动时间,减少的时间比起我们改造的成本来说,收益大不大,是不是最优解,都是我们需要着重考虑的问题,所以,如何选择合适的线程池是最先需要解决的问题。
3.5 修改消息队列
- https://mp.weixin.qq.com/s/Jaszg1Tjqj6DvMJYZoy96g
04.启动优化实践操作
4.4 任务调度实践
- 选择何时调度线程池
- 线程,它是CPU任务调度的基本单位,而并发的本质就是共享CPU的时间片,所以,如果在线程池中的任务极大的消耗了CPU的资源,这就会导致一个直观的问题,看似串行任务变成了多线程并发任务,却造成了主线程卡顿,导致我们的所作所为出现了副作用。
- 关于线程池的选择,在这种场景下,最优的选择无非就是定容线程池,缓存线程池,但是到底该用哪种,又或者说两者都用的时候,任务该如何决定使用哪个线程池,发现问题就预示着我们已经解决了该问题的一半。
- 如何确定选择任务是CPU型还是IO型任务
- 一个任务是否是CPU消耗型的任务(比如运算类的操作),还是说IO类型的任务(内存分配型),前者消耗的CPU时间片较多,我们就把它放在定容线程池里调度,后者消耗的时间片少,就把它放在缓存线程池中,技能充分的调用CPU资源,又不容易过度占用CPU,使得任务并发运行,达到时间优化的目的。
- 怎么确定一个任务耗时分析
- 借助工具:SysTrace。待完善
4.5 并发控制顺序
- 多线程并发时,并且使用的不同的线程池后,这些任务执行的顺序问题又该如何解决呢。
- 有向无环图这个数据结构完美的解决了我们的问题。其实就是每个任务用countDownLatch来标记入度。如下所示
任务1 任务2 任务3 任务4 任务5 任务6 任务7
- 任务执行等待问题
- 实际开发中经常会遇到这种场景,SplashActivity的启动必须依赖于某个库初始化完成才行,简单来说就是在application中阻塞执行这个任务。
- 基于多线程并发任务调度,最简便的方法就是任务管理器使用CountDownLatch,在任务开始执行时调用countDownLatch.await(),在构造图结构时,把需要在application中阻塞执行的任务标记好,然后每执行完一个任务countDownLatch.countDown(),直到所有阻塞任务都执行完毕后,阻塞结束。
4.7 App启动白屏优化
4.7.1 启动白屏说明
存在白屏问题描述
android app启动页面黑屏的问题,android开发app启动时若没有做特殊处理的话,会出现一瞬间的白屏现象。即使你启动页界面就加载一个布局,不做其他耗时处理,貌似也会出现一瞬间的白屏问题。注意,有些地方也称黑屏,主要是看你给app设置的style样式。
为什么存在这个问题
当系统启动一个APP时,zygote进程会首先创建一个新的进程去运行这个APP,但是进程的创建是需要时间的,在创建完成之前,界面是呈现假死状态,于是系统根据你的manifest文件设置的主题颜色的不同来展示一个白屏或者黑屏。而这个黑(白)屏正式的称呼应该是Preview Window,即预览窗口。
实际上就是是activity默认的主题中的android:windowBackground为白色或者黑色导致的。总结来说启动顺序就是:app启动——Preview Window(也称为预览窗口)——启动页
4.7.2 设置背景style样式
解决方案分析
Android在选择展示黑屏或者白屏的时候,是根据你设定的主题而不同的,也就是说,虽然你的代码没有被执行,你的配置文件却被提前读取了,用来作为展示Preview Window界面的依据。所以,解决方案的切入口就是整个APP的manifest文件,更确切的说应该是主题配置文件。
设置配置文件style样式中的windowBackground这个属性来显示一张背景图还有一个效果就是启动应用程序会感觉非常快,而且与加载MainActivity的contentView是异步的。
解决方案:给当前启动页添加一个有背景的style样式。设置style样式如下:
<style name="SplashTheme" parent="AppTheme">
<item name="android:windowBackground">@mipmap/splash</item>
<item name="android:statusBarColor" tools:ignore="NewApi">@color/white</item>
<item name="android:windowIsTranslucent">true</item>
</style>
注意,在清单文件中
<activity android:name=".SplashActivity"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
经过处理之后App启动时就不会出现一瞬间白屏的效果。将主题设置到启动的Activity的主题中,windowBackground就是即将展示的preview window。其中splash可以是一整张图片,它也可以是一个能解析出图片资源的XML文件。
该方案注意要点:给Preview Window设置的背景图如果不做处理,图片就会一直存在于内存中,所以,当我们进入到欢迎页的时候,不要忘了把背景图设置为空
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
//将window的背景图设置为空
getWindow().setBackgroundDrawable(null);
super.onCreate(savedInstanceState);
}
这样操作如何屏幕适配呢?这样通过样式style设置SplashActivity加载图,不能像imageView那样可以设置缩放功能,因此可以采用.9图片。以前有开发者采用我的这个建议,直接设置图,没有做适配,也无伤大雅,具体要看UI要求呢!
4.7.3 禁止加载Preview
禁止加载Preview Window,具体做法如下:
<style name="SplashTheme" parent="@style/Theme.AppCompat.Light.NoActionBar">
<item name="android:windowDisablePreview">true</item>
</style>
设定为启动的Activity的主题,即可禁止Preview Window,当然,也有人通过把preview window设置为全透明,也达成了类似的效果。个人感觉这种方法没有第一种好!
windowDisablePreview的作用:通过设置android:windowDisablePreview属性,禁用窗口的预览动画,在SplashActivity显示之前,系统永远不会使用窗口的主题来显示它的预览,这也保证了不会出现白屏或者黑屏。但是,与设置android:windowIsTranslucent属性一样,如果在SplashActivity启动的时候,有过多复杂的操作,就会出现在手机中点击了应用程序的图标,但过n秒才会打开应用程序不好的卡顿体验效果。
该方案是否有缺点?这种方法有个小缺点,就是点击后短暂的那几百毫秒没有反应,就好像“假死”了一样,过了一会儿才跳出我们应用程序的第一个Activity,如果你不想让你的 App 有这个短暂“假死”时间,建议使用第一种方法。
07.启动时长优化措施
- 举个例子,比如优化启动Application,那么把每一个函数详细方法罗列出现,然后拿到时间消耗。
- 第一步统计各个页面,各个方法损耗时间
- 统计数据
- 第二步,某些初始化是否可以异步
- 是否可以异步
- 第三步,优化前后的时间对比,变化多大,要求可以衡量
- 优化前后时间对比
07.启动优化总结
7.1 启动优化怎么做
- 启动优化是怎么做的?
- 分析现状、确认问题
- 针对性优化(先概括,引导其深入)
- 长期保持优化效果
- 启动流程分析概括
- 代码层面分析,启动流程非常复杂,发现主线程任务太多。
- 发现有些任务初始化优先级并不是那么高,可以不放在onCreate中执行,觉得可以放在之后延迟执行。
- 如何保证长期启动优化效果
- 做了我们的启动器,并且在线上加上了很多方面的监控。
7.2 如何异步策略
- 异步演进的过程
- 1.最初是采用的普通的一个异步的方案,即new Thread + 设置线程优先级为后台线程的方式在Application的onCreate方法中进行异步初始化。
- 2.使用了线程池、IntentService的方式,但在应用的演进过程当中,发现代码会变得不够优雅,并且有些场景非常不好处理,比如说多个初始化任务直接的依赖关系,比如说某一个初始化任务需要在某一个特定的生命周期中初始化完成,这些都是使用线程池、IntentService无法实现的。
- 使用启动器优化
- 在启动器的概念中,将每一个初始化代码抽象成了一个Task,然后,对它们进行了一个排序,根据它们之间的依赖关系排了一个有向无环图。
7.3 忽略的注意点
- 启动优化有哪些容易忽略的注意点?
- cpu time与wall time
- 注意延迟初始化的优化
- 注意两个很重要的指标,即cpu time与wall time
- 必须清楚cpu time与wall time之间的区别,wall time指的是代码执行的时间,而cpu time指的是代码消耗CPU的时间,锁冲突会造成两者时间差距过大。需要以cpu time来作为我们优化的一个方向。
- 使用了idealHandler来实现cpu空闲时间来执行耗时任务,这极大地提升了用户的体验,避免了因启动耗时任务而导致的页面卡顿现象。
7.4 迭代优化方案
- 版本迭代导致的启动变慢有好的解决方式吗?
- 启动器
- 监控完善
- 使用启动器管理每一个初始化任务
- 启动器中每一个任务的执行都是被其自动进行分配的,也就是说这些自动分配的task我们会尽量保证它会平均分配在我们每一个线程当中的,这和我们普通的异步是不一样的,它可以很好地缓解我们应用的启动变慢。
- 监控也需要不断完善
- 进行了监控的一个完善,将每一个生命周期都进行了一个监控。
7.5 启动效果分析
参考和学习
- 抖音 Android 性能优化系列:启动优化实践:https://juejin.cn/post/7080065015197204511
- 启动优化之理论和工具篇:https://juejin.cn/post/7058080006022856735
- https://github.com/DSAppTeam/Anchors
- Android启动优化
- https://copyfuture.com/blogs-details/202207212050137974
- 深入研究Android启动速度优化(下)- 不敢说100%秒开,但这样做“雀食”是快
- https://juejin.cn/post/7359347999309807654
秒开
- https://zhengxiaoyong.com/2016/07/18/Android%E7%AB%AF%E5%BA%94%E7%94%A8%E7%A7%92%E5%BC%80%E4%BC%98%E5%8C%96%E4%BD%93%E9%AA%8C/
参考
- https://mp.weixin.qq.com/s?__biz=MzAxMTI4MTkwNQ==&mid=2650829130&idx=1&sn=b63f6fc838d81280f0b45822a2b6ef79&chksm=80b7a5d4b7c02cc26b7f37f6fd77d9b04ea4e72b326684cfb20687f3872e231242eb3996a59a&scene=21#wechat_redirect
- https://blog.csdn.net/u011578734/article/details/110002688
- https://github.com/bingoogolapple/AppInit
- https://github.com/aiceking/AppStartFaster
- 抖音 Android 性能优化系列:启动优化之理论和工具篇
- https://juejin.cn/post/7058080006022856735
- 抖音 Android 性能优化系列:启动优化实践
- https://juejin.cn/post/7080065015197204511
- 货拉拉用户端体验优化--启动优化篇
- https://juejin.cn/post/7087773731061235743
- Android启动优化实践 - 秒开率从17%提升至75%
- https://mp.weixin.qq.com/s/ZSKE1EoV8EjU9EN8ZvDy3A
- 得物App安卓冷启动优化-Application篇
- https://mp.weixin.qq.com/s/f9ng0wabLwwXNfCO00qtPQ