事件发生在发包上线的前两天,在某某云进行移动测试时,提示冷启动速度低于平均值的问题,以前本身也曾尝试过优化,可是发现效果并非很明显,做为一个有追求的开发者,趁着有点空闲时间,要好好研究一下冷启动优化问题。android
咱们能够了解一下官方文档《App startup time》对App启动的描述。应用启动分为冷启动、热启动、温启动。而冷启动是应用程序从零开始,里面涉及到更复杂的知识。咱们此次主要是对应用的冷启动进行分析和优化。应用在冷启动的时候,须要执行下面三个任务:git
在这三个任务执行后,系统建立了应用进程,那么应用进程会执行下一步:github
当应用进程完成初始绘制以后,系统进程用启动页的Activity来替换当前显示的背景窗口,这个时刻用户就可使用App了。下图显示为系统和应用程序的工做流程。shell
从上图和上述的步骤咱们能够知道,应用进程的建立,那么它确定会执行咱们的Application的生命周期,当建立完成App的应用进程以后,主线程会初始化咱们第一个页面MainActivity与执行MainActivity的生命周期。我特地加粗了重点,这就是咱们能够下手优化的部分。在分析如何优化前,咱们能够先了解一下,咱们的应用是否是须要对冷启动进行优化。数据库
PS:其实这些都是咱们表面看到的东西,若是咱们须要完整地去深究,咱们要去具体分析Zygote Fork进程、ActivityManagerService源码等,咱们就不在该篇中详述,给你们推荐相关书籍,有罗升阳的《Android系统源代码情景分析》,刘望舒的《Android进阶解密》。bash
那么启动时间多少才是合适呢?在官方文档中描述到当冷启动在5秒或者更长的时,Android vitals就会认为你的应用须要进行冷启动相关的优化。不过Android vitals是针对Google Play的一款应用质量检测工具,那你们都明白,不过你能够像我同样使用阿里云的移动测试,阿里云提供的数据中,冷启动的行业指标中位数是4875.67ms,你们能够酌情对比一下。好了,下面咱们就聊一下若是检测出咱们应用的冷启动时间。网络
如上图一显示的Displayed Time,在Android 4.4(API级别19)及更高版本中,logcat包含一个名为Displayed的log信息,此值表示启动过程和完成在屏幕上绘制相应活动之间所通过的时间量。app
adb shell am start -W [packageName]/[packageName.MainActivity]
复制代码
在使用上一个方式Displayed Time的log打印台,咱们看到Displayed的log,后面跟着就是下面咱们须要的[packageName]/[packageName.MainActivity],咱们能够直接复制使用,而后我 们在AS的Terminal中粘贴,接着打印的就是咱们指定页面的启动时间数据。异步
Status: ok
Activity: com.xx.xxx/com.xx.xxxx.welcome.view.WelcomeActivity
ThisTime: 242
TotalTime: 242
WaitTime: 288
Complete
复制代码
在某些特殊场景,咱们可能不仅仅启动页的绘制完成回调时间就足够了,咱们须要连启动页的闪屏广告接口数据成功回调以后才算一个完整的时间,这时咱们可使用reportFullyDrawnide
public class WelcomeActivity extends MvpActivity implements WelcomeMvp.View {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_welcome);
// 请求数据
mvpPresenter.config();
}
@Override
public void finishRequest() {
// 数据回调
reportFullyDrawn();
}
}
复制代码
Traceview是Android设备的一个很是好用的性能分析工具,它能够经过详细的界面,让咱们跟踪程序的性能,而且能清晰地查看到每个函数的耗时和调用次数。
Systrace很是直观地展现每一个线程上面的API的调用顺序和耗时状况。
Traceview和Systrace都是DDMS面板的工具,可是如今AS3.0以上的版本再也不建议使用了,因此这里就不详述,若是有兴趣的同窗,能够看我上一篇文章《Android应用优化之流畅度实操》,里面有详细地说明这两个工具的用法。
github.com/JakeWharton…
咱们能够利用JakeWharton的hugo,经过注解的方式获取对应的类或者函数所消耗的时间。咱们能够利用它对启动页Activity的生命周期来抠细节。
在冷启动优化的主要体验我的认为就是消除启动时的白屏/黑屏,由于白屏/黑屏对于用户使用的第一印象就是慢、卡顿。咱们能够设置启动页的主题来达到目的。
<style name="WelcomeTheme" parent="Theme.AppCompat.Light.NoActionBar.FullScreen">
<item name="android:windowBackground">@drawable/shape_welcome</item>
<item name="android:windowDrawsSystemBarBackgrounds">false</item>
</style>
复制代码
windowDrawsSystemBarBackgrounds是对部分有系统操做栏的设置。接着是这个窗口背景色的布局。
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" android:opacity="opaque">
<item android:drawable="@android:color/white"/>
<item>
<bitmap
android:src="@drawable/welcome_bg"
android:gravity="center"/>
</item>
</layer-list>
复制代码
启动页的广告展现完跳转到首页,而后咱们设置回咱们的通用样式,能够在清单文件,也能够在代码中设置。
<activity
···
android:theme="@style/AppBaseFrameTheme"/>
复制代码
经过对启动页的主题设置后,就会将白屏/黑屏抹去,用户点击App的图标就展现启动图,让用户先产生启动很快的“错觉”。同时这里能够经过动画,让启动页与首页之间的过渡更加天然。
从上图一的分析总结中,我对优化点Application的生命周期进行了加粗提示,接着咱们回来对这部分进行优化实操。
1. Application#attachBaseContext()
Application启动会通过attachBaseContext()-->onCreate();这时你们从attachBaseContext的生命周期联想到什么?没错就是MultiDex分包机制。想必你们都会发现,自从咱们方法数超出了65535处理了分包以后,启动白屏/黑屏的问题就出现了,分包机制是致使冷启动缓慢的重要缘由,而如今部分应用采用插件化的方式来避免MultiDex带来的白屏问题,这虽然是一种方法,可是开发成本实在高,对于很多应用来讲是没必要要的。咱们来聊一下MultiDex优化,首先MultiDex可分红运行时和编译时两个部分:
2. Application#onCreate()
通过attachBaseContext()后就到onCreate()生命周期,想必咱们大部分的应用,会在这里对咱们使用到的第三方库和组件进行初始化工做。因为版本不断迭代,第三方库的初始化都是直接写在onCreate()中,大量的初始化工做致使该生命周期过于沉重,咱们应该对这些第三方库进行分类。下面是我整理我司App启动的工做分类:
你们能够根据自身项目先列出本身项目的每个初始化,而后进行分类。这里虽然我没有贴具体的操做代码,不是我认为new一个线程或者建立一个IntentService太简单了就不说了,而是这里须要注意的东西是整个冷启动优化最多的,由于本身也在这里踩过坑。 举一个GrowingIO的例子,当时项目用的是很旧版本的GIO,当时对GIO的初始化是放在子线程操做的,突然发包前,运营部门提出升级GIO的SDK版本需求,升完以后编译运行以为没什么事情就直接打包了,到线上以后运营反馈新版本没了圈选数据,通过检查发现新版本的GIO是不能在子线程初始化的。从这个教训中,我认为既然同窗你都对冷启动优化感兴趣,因此必定不会差那几句复制粘贴的代码,这些都是要具体状况具体分析。我来总结一下重点
1.布局优化 咱们的启动页Activity包含有启动图控件、闪屏广告图控件、闪屏广告视频控件、首次安装介绍图控件。对于布局优化而言,除了启动图控件外,其余都不是App启动时都要初始化的控件,这时咱们可使用ViewStub。针对指定的业务场景,初始化指定的控件。 2.避免I/O操做 咱们知道I/O操做不是实时的,例如数据库的读写、SharedPreferences#apply()。咱们要注意这些操做有没阻塞主线程地执行,同时咱们能够利用StrictMode严格模式,利用它能够检测咱们在启动的时候有没正确进行磁盘读写操做。 3.注意图片bitmap的加载速度和编码格式 咱们能够知道,启动页大部分的状况下都是图片的显示,那么咱们在图片这方面怎么抠细节呢,那就是对各类第三方图片加载库的选用了Glide、Picasso、Fresco等,还有是PREFER_ARGB_888八、PREFER_RGB_565的选取问题,你们能够针对属于本身项目状况进行选取。 4.对矢量图VectorDrawable对象的使用 矢量图的核心是省时间、省空间。而对于某些用户,它的启动图可能不是一张图片,它十分简约,就一个logo,这个时候咱们能够考虑一下矢量图的用法。 5.注意Activity中的启动生命周期的回调 咱们在Application#onCreate()优化,将某些不是很必要的网络请求,搬到了欢迎页中,可是咱们也不能直接将这个网络请求操做直接拷贝到启动页的onCreate()中,咱们能够巧妙地利用Activity生命周期中的Activity#onWindowFocusChanged(boolean hasFocus) ,这个是全部控件初始化完的真正回调,咱们能够将网络操做放在这里,固然咱们还可使用Service。
对于冷启动优化,须要咱们一步步去分析,不像布局优化那般照搬套路,因此在官方文档中也屡次出现bottleneck瓶颈这个词汇,说明了咱们的冷启动优化之路不会一马平川,你们要善用Android Studio‘s CPU profiler(有机会咱们详细分析一下该功能的使用),由于网上不少的总结是经过Traceview和Systrace,可是这二者在AS3.0版本的升级已经舍弃,侧面反映到咱们要勤看官方文档,用本身的第一角度去思考Android的变化,而不是经过别人的翻译分析。最后你们互相勉励一下,在如今的Android市场竞争愈发激烈,如何在竞品对比中胜出,还须要咱们一步步地把一个个的细节作好作完美。
若是喜欢本文的话,欢迎点击一下 “喜欢” 点个赞给予鼓励支持!