你必须掌握的Android冷启动优化,但是发现效果不是很明显,作为一个有追求的开发者,趁着空闲时间,好好研究一下冷启动优化问题。App启动流程我们可以看看官方文档《App startup time》中关于App启动的描述。应用启动分为冷启动、热启动和热启动。冷启动是应用程序从头开始,涉及的知识比较复杂。这次我们主要分析和优化应用的冷启动。应用冷启动时,需要执行以下三个任务:加载并启动应用;应用程序启动后立即显示一个空白的启动窗口;创建应用程序的进程;这三个任务执行完后,系统就创建了应用进程,然后应用进程会执行下一步:创建App对象;启动主线程;创建启动页的Activity;加载视图;替换当前显示的背景窗口,此时用户可以使用App。下图显示了系统和应用程序的工作流程。从上图和上面的步骤我们可以知道,应用进程的创建肯定会执行我们Application的生命周期。App的应用进程创建完成后,主线程会初始化我们的第一个页面MainActivity,并执行MainActivity的生命周期。我特意把重点加粗了,这是我们可以优化的部分。在分析如何优化之前,我们可以先了解一下我们的应用是否需要针对冷启动进行优化。PS:其实这些都是我们表面上看到的东西。如果要完整研究,需要详细分析Zygote的Fork流程、ActivityManagerService源码等。洛盛阳的《Android系统源代码情景分析》,刘望舒的?。启动时间检测那么启动时间多少合适呢?根据官方文档,当冷启动时间为5秒或更长时,Androidvitals会认为你的应用程序需要进行冷启动优化。不过Androidvitals是GooglePlay的一款应用质量检测工具,这个大家都懂,不过大家可以像我一样使用阿里云的移动端测试。阿里云提供的数据中,冷启动行业指标的中位数为4875.67ms,大家可以酌情对比。好吧,让我们谈谈如何检测我们应用程序的冷启动时间。显示时间如上图1所示。在Android4.4(API级别19)及更高版本中,logcat包含名为Displayed的日志信息。该值表示从启动过程到在屏幕上绘制相应活动完成之间的时间。经过的时间。adbcommandadbshel??lamstart-W[packageName]/[packageName.MainActivity]就是使用了前面方法中DisplayedTime的日志打印站,我们看到Displayed的log,下面是我们需要的[packageName]/[packageName.MainActivity],我们可以直接复制使用,然后我们在AS的Terminal中粘贴,然后打印我们指定页面的启动时间数据。Status:okActivity:com.xx.xxx/com.xx.xxxx.welcome.view.WelcomeActivityThisTime:242TotalTime:242WaitTime:288CompleteThisTime:指调用过程中最后一个Activity开始到startActivityAndWait调用结束的时间这个活动;TotalTime:指调用进程中第一个Activity开始到最后一个Activity的startActivityAndWait结束的时间。WaitTime:调用startActivityAndWait方法耗时;reportFullyDrawn在一些特殊的场景下,我们可能不仅回调启动页的绘制完成时间就够了,还需要回调启动页的闪屏广告界面数据算作一次完成,那么我们就可以使用reportFullyDrawnpublicclassWelcomeActivityextendsMvpActivityimplementsWelcomeMvp.View{@OverrideprotectedvoidonCreate(@NullableBundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.Presen.activityconfig);/com请求}@OverridepublicvoidfinishRequest(){//数据回调reportFullyDrawn();}}PS:该方法的minSdkVersion要求API19+,所以需要设置或判断SDK版本。TraceviewTraceview是一款非常好用的Android设备性能分析工具。它可以让我们通过一个详细的界面来跟踪程序的性能,可以清晰的查看每个函数的耗时和调用次数。SystraceSystrace非常直观的展示了各个线程上面API的调用顺序和耗时情况。Traceview和Systrace都是DDMS面板的工具,但是不再推荐使用AS3。详细描述这两个工具的用法。hugogithub.com/JakeWharton...我们可以使用JakeWharton的hugo,通过注解获取对应类或函数消耗的时间。我们可以通过它来挖掘启动页Activity的生命周期细节。启动优化实用用户体验优化冷启动优化的主要体验是消除启动时的白屏/黑屏,因为白屏/黑屏给用户的第一印象是缓慢和卡顿。我们可以通过设置启动页的主题来达到目的。@drawable/shape_welcomefalsewindowDrawsSystemBarBackgrounds是一些系统操作栏的设置。接下来是窗口背景颜色的布局。 启动页的广告展示后,跳转到首页,然后我们设置回我们一般的样式,可以在manifest文件中设置,也可以在代码中设置。onCreate();这时候从attachBaseContext的生命周期想到什么呢?没错,就是MultiDex的分包机制。想必大家会发现,由于我们处理分包的方法数超过了65535个,就出现了启动白屏/黑屏的问题。分包机制是冷启动慢的一个重要原因,现在有些应用采用插件的方式。避免MultiDex引起的白屏问题,虽然这是一种方法,但是开发成本确实很高,对于很多应用来说是没有必要的。我们来谈谈MultiDex优化。首先,MultiDex可以分为运行时和编译时两部分:编译时:将App中的classes按照一定的策略拆分成多个dex。为了减少第一个dex,maindex中包含的类的数量;运行时:App启动时,虚拟机只加载maindex中的类。App启动后,使用Multidex.install通过反射机制修改ClassLoader中的dexElements加载其他dex;从网上很多文章的实际分析来看,主要是采用异步的方式。因为App一开始会先加载主dex包,然后我们就可以独立处理分包工作了。我们将启动页和首页需要的库、组件等主要类划分到主dex中,从而实现主dex包的精确划分。大小,具体操作和写法,可以参考网上的MultiDex启动优化文章,但是在拆分主dex的过程中要注意主dex,主dex经过了一系列的优化操作,减小了大小maindex的,所以也增加了NoClassDefFoundError异常的可能性会增加,从而导致我们的应用启动失败的风险,所以一定要做好优化后的测试工作。2、Application#onCreate()会在attachBaseContext()之后进入onCreate()生命周期。想必我们大部分的应用程序都会初始化我们这里使用的第三方库和组件。由于版本不断迭代,第三方库的初始化直接写在onCreate()中。大量的初始化工作使得生命周期过于繁重。我们应该对这些第三方库进行分类。以下是我整理我们公司App启动的工作分类:看上图,各种第三方工具初始化和业务逻辑初始化影响启动时间。我们首先将它们分成四个部分。必须在onCreate()中初始化,主流程可以延迟,但是如果需要在Application中初始化,可以延迟到初始化延迟到启动页的生命周期回调中使用。可以根据自己的项目先列出自己项目的每一项。一个初始化,然后分类。这里虽然没有贴出具体的操作代码,但并不是我觉得新建一个线程或者创建一个IntentService太简单了,而是这里需要注意的是最优化的冷启动,因为我也踩了就在这里坑吧。举个GrowingIO的例子,项目当时用的是一个很老版本的GIO。当时GIO的初始化都是放在子线程运行的。突然,合同还没发,运营部就提出要升级GIO的SDK版本。升级后编译运行。没事儿就直接打包。上线后运营反馈新版本无圈选数据。检查后发现新版本的GIO无法在子线程中初始化。从这节课来看,我想既然大家??都对冷启动优化感兴趣,那复制粘贴的那几行代码就一定不能错过。这些都是需要具体分析的具体情况。我总结一下慢启动的要点,不要想都没想就启动一个线程,然后塞代码就完事了,需要对症下药;开线程也是一门学问,Thread、ThreadPoolExecutor、AsyncTask、IntentService,选哪个;假设你有一个新的Thread,但是你有没有考虑过内存泄漏的问题,不要边挖边填坑;注意部分第三方SDK需要在主线程中初始化;如果应用是多进程的,注意一些第三方SDK要求你在同一个包名下,其实有很多项目。经过多年的版本迭代,代码一直没有整理出来。那些旧的代码和无用的代码需要被清理掉。优化启动页Activity的布局。我们的启动页Activity包含启动图控件、闪屏广告图控件、闪屏广告视频控件、***安装介绍图控件。布局优化,除启动地图控件外,其他控件在App启动时不初始化。这时候我们就可以使用ViewStub了。针对指定的业务场景,初始化指定的控件。避免I/O操作我们知道I/O操作不是实时的,比如数据库读写,SharedPreferences#apply()。我们要注意这些操作是否会阻塞主线程的执行。同时我们可以使用StrictMode严格模式,它可以用来检测我们在启动时是否正确进行了磁盘读写操作。注意图像位图的加载速度和编码格式。我们可以知道,大部分的启动页都是用图片显示的。那么我们如何挖掘出图像中的细节呢?那就是各种第三方图片加载库的选择。Glide、Picasso、Fresco等,还有PREFER_ARGB_8888和PREFER_RGB_565的选择,大家可以根据自己的项目情况选择。矢量图形的VectorDrawable对象的使用矢量图形的核心是节省时间和空间。对于一些用户来说,它的启动图片可能不是图片,很简单,只是一个logo,这时候我们可以考虑矢量图的使用。注意Activity中启动生命周期的回调在Application#onCreate()中进行了优化,将一些不需要的网络请求移到了欢迎页中,但是我们不能直接在onCreate()中将网络请求操作复制到启动页中,我们可以巧妙地在Activity生命周期中使用Activity#onWindowFocusChanged(booleanhasFocus)。这是所有控件初始化后真正的回调。我们可以把网络操作放在这里,当然我们也可以使用Service。冷启动优化总结对于冷启动优化,我们需要一步一步来分析,不像布局优化,所以“瓶颈”这个词在官方文档中多次出现,可见我们的冷启动优化之路不会完全一样,大家要好好利用AndroidStudio的CPUprofiler(有机会再详细分析一下这个功能的使用),因为网上很多总结都是通过Traceview和Systrace,但是这两个在AS3.0已经被废弃了版本升级。从侧面反映出我们要经常阅读官方文档,站在自己的角度去思考Android的变化,而不是通过别人的翻译分析。***互相鼓励,现在Android市场的竞争越来越激烈,如何在竞品对比中胜出,还是需要一步一个脚印做好每一个细节***。