Android冷启动优化解析

简介: 前言事件发生在发包上线的前两天,在某某云进行移动测试时,提示冷启动速度低于平均值的问题,之前自己也曾尝试过优化,但是发现效果并不是很明显,作为一个有追求的开发者,趁着有点空闲时间,要好好研究一下冷启动优化问题。

前言

事件发生在发包上线的前两天,在某某云进行移动测试时,提示冷启动速度低于平均值的问题,之前自己也曾尝试过优化,但是发现效果并不是很明显,作为一个有追求的开发者,趁着有点空闲时间,要好好研究一下冷启动优化问题。

App的启动流程

我们可以了解一下官方文档《App startup time》对App启动的描述。应用启动分为冷启动、热启动、温启动。而冷启动是应用程序从零开始,里面涉及到更复杂的知识。我们这次主要是对应用的冷启动进行分析和优化。应用在冷启动的时候,需要执行下面三个任务:

  • 加载和启动应用程序;
  • App启动之后立即展示出一个空白的启动窗口;
  • 创建App程序的进程;

在这三个任务执行后,系统创建了应用进程,那么应用进程会执行下一步:

  • 创建App对象;
  • 启动Main Thread;
  • 创建启动页的Activity;
  • 加载View;
  • 布置屏幕;
  • 进行初始绘制;

当应用进程完成初始绘制之后,系统进程用启动页的Activity来替换当前显示的背景窗口,这个时刻用户就可以使用App了。下图显示为系统和应用程序的工作流程。

 

从上图和上述的步骤我们可以知道,应用进程的创建,那么它肯定会执行我们的Application的生命周期,当创建完成App的应用进程之后,主线程会初始化我们第一个页面MainActivity与执行MainActivity的生命周期。我特意加粗了重点,这就是我们可以下手优化的部分。在分析如何优化前,我们可以先了解一下,我们的应用是不是需要对冷启动进行优化。

PS:其实这些都是我们表面看到的东西,如果我们需要完整地去深究,我们要去具体分析Zygote Fork进程、ActivityManagerService源码等,我们就不在该篇中详述,给大家推荐相关书籍,有罗升阳的《Android系统源代码情景分析》,刘望舒的《Android进阶解密》。

启动时间检测

那么启动时间多少才是合适呢?在官方文档中描述到当冷启动在5秒或者更长的时,Android vitals就会认为你的应用需要进行冷启动相关的优化。不过Android vitals是针对Google Play的一款应用质量检测工具,那大家都明白,不过你可以像我一样使用阿里云的移动测试,阿里云提供的数据中,冷启动的行业指标中位数是4875.67ms,大家可以酌情对比一下。好了,下面我们就聊一下如果检测出我们应用的冷启动时间。

Displayed Time

如上图一显示的Displayed Time,在Android 4.4(API级别19)及更高版本中,logcat包含一个名为Displayed的log信息,此值表示启动过程和完成在屏幕上绘制相应活动之间所经过的时间量。

 

ADB命令

 
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 
  • ThisTime:是指调用过程中最后一个Activity启动时间到这个Activity的 startActivityAndWait调用结束;
  • TotalTime:是指调用过程中第一个Activity的启动时间到最后一个Activity的 startActivityAndWait结束。
  • WaitTime:是startActivityAndWait这个方法的调用耗时;

reportFullyDrawn

在某些特殊场景,我们可能不单单启动页的绘制完成回调时间就足够了,我们需要连启动页的闪屏广告接口数据成功回调之后才算一个完整的时间,这时我们可以使用reportFullyDrawn

 
public class WelcomeActivity extends MvpActivity<WelcomePresenter> 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(); } } 

 

PS:这个方式minSdkVersion需要API19+,所以要对SDK版本进行设置或判断。

Traceview

Traceview是Android设备的一个非常好用的性能分析工具,它可以通过详细的界面,让我们跟踪程序的性能,并且能清晰地查看到每一个函数的耗时和调用次数。

Systrace

Systrace非常直观地展示每个线程上面的API的调用顺序和耗时情况。

Traceview和Systrace都是DDMS面板的工具,但是现在AS3.0以上的版本不再建议使用了,所以这里就不详述,如果有兴趣的同学,可以看我上一篇文章《Android应用优化之流畅度实操》,里面有详细地说明这两个工具的用法。

hugo

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启动优化

从上图一的分析总结中,我对优化点Application的生命周期进行了加粗提示,接着我们回来对这部分进行优化实操。

  1. Application#attachBaseContext()

Application启动会经过attachBaseContext()-->onCreate();这时大家从attachBaseContext的生命周期联想到什么?没错就是MultiDex分包机制。想必大家都会发现,自从我们方法数超出了65535处理了分包之后,启动白屏/黑屏的问题就出现了,分包机制是导致冷启动缓慢的重要原因,而现在部分应用采用插件化的方式来避免MultiDex带来的白屏问题,这虽然是一种方法,但是开发成本实在高,对于不少应用来说是不必要的。我们来聊一下MultiDex优化,首先MultiDex可分成运行时和编译时两个部分:

  • 编译期:将App中的class以某种策略拆分在多个dex中,为了减少第一个dex也就主dex中包含的class数;
  • 运行期: App启动时,虚拟机只加载主dex中的class。app启动以后,使用Multidex.install,通过反射机制修改ClassLoader中的dexElements来加载其他dex;

从网上的多篇实践分析中,他们主要采用的是异步方式。因为App起始会先加载主dex包,那么我们可以自主去处理分包的工作,我们将启动页和首页需要的库、组件等主要class分在主dex中,从而达到精分主dex包的大小,具体的操作写法,大家可以参考网上MultiDex启动优化文章,但是大家要注意在主dex的分包过程中,主dex经过我们一系列的优化操作减少了主dex的大小,因此也增大了NoClassDefFoundError的异常的可能,此时会导致我们的应用启动失败的风险,所以在优化后我们一定做好测试工作。

  1. Application#onCreate()

经过attachBaseContext()后就到onCreate()生命周期,想必我们大部分的应用,会在这里对我们使用到的第三方库和组件进行初始化工作。由于版本不断迭代,第三方库的初始化都是直接写在onCreate()中,大量的初始化工作导致该生命周期过于沉重,我们应该对这些第三方库进行分类。下面是我整理我司App启动的工作分类:

 

 

看着上图,各种第三方工具初始化和业务逻辑初始化,影响启动时间。我们先对它们拆分成四部分。

  • 必须在onCreate()且是主进程中初始化
  • 可以延迟,但是需要在Application中初始化
  • 可以延迟到启动页的生命周期回调中初始化
  • 延迟到用的时候再初始化

大家可以根据自身项目先列出自己项目的每一个初始化,然后进行分类。这里虽然我没有贴具体的操作代码,不是我认为new一个线程或者创建一个IntentService太简单了就不说了,而是这里需要注意的东西是整个冷启动优化最多的,因为自己也在这里踩过坑。 举一个GrowingIO的例子,当时项目用的是很旧版本的GIO,当时对GIO的初始化是放在子线程操作的,忽然发包前,运营部门提出升级GIO的SDK版本需求,升完之后编译运行觉得没什么事情就直接打包了,到线上之后运营反馈新版本没了圈选数据,经过检查发现新版本的GIO是不能在子线程初始化的。从这个教训中,我认为既然同学你都对冷启动优化感兴趣,所以一定不会差那几句复制粘贴的代码,这些都是要具体情况具体分析。我来总结一下重点

  • 启动慢,不是无脑开线程,然后塞代码就完事,需要对症下药;
  • 开线程也是一门学问,Thread、ThreadPoolExecutor、AsyncTask、IntentService,究竟选取哪个;
  • 假设你new好了Thread,但是有没考虑好内存泄漏问题,不要一边补坑一边挖坑;
  • 注意有些第三方SDK需要在主线程初始化的;
  • 如果是应用是多进程的,注意有些第三方SDK,需要你在跟同包名进程下进行初始化;
  • 其实有好多项目,经过多年的版本迭代都是没有整理过代码的,那些旧代码、无用代码都是需要归类整理的;

启动页Activity的优化

  1. 布局优化 我们的启动页Activity包含有启动图控件、闪屏广告图控件、闪屏广告视频控件、首次安装介绍图控件。对于布局优化而言,除了启动图控件外,其他都不是App启动时都要初始化的控件,这时我们可以使用ViewStub。针对指定的业务场景,初始化指定的控件。
  2. 避免I/O操作 我们知道I/O操作不是实时的,例如数据库的读写、SharedPreferences#apply()。我们要注意这些操作有没阻塞主线程地执行,同时我们可以利用StrictMode严格模式,利用它可以检测我们在启动的时候有没正确进行磁盘读写操作。
  3. 注意图片bitmap的加载速度和编码格式 我们可以知道,启动页大部分的情况下都是图片的显示,那么我们在图片这方面怎么抠细节呢,那就是对各种第三方图片加载库的选用了Glide、Picasso、Fresco等,还有是PREFER_ARGB_8888、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市场竞争愈发激烈,如何在竞品对比中胜出,还需要我们一步步地把一个个的细节做好做完美。

Android app 全方位性能调优;

  • 代码结构优化
  • 用户体验及消耗资源优化
  • 屏幕适配优化
  • 代码质量调优

 

关注+转发后,加Android高级开发QQ群;701740775。即可获取以上视频资料! 以及往期Android高级架构资料、高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)等视频资料

本群提供免费的学习指导 架构资料 以及免费的解答,不懂得问题都可以在本群提出来

之后还会有职业生涯规划以及面试指导,加群请备注 csdn

相关文章
|
5天前
|
Java Android开发
Android12 双击power键启动相机源码解析
Android12 双击power键启动相机源码解析
15 0
|
1天前
|
机器学习/深度学习 人工智能 算法
构建高效AI系统:深度学习优化技术解析
【5月更文挑战第12天】 随着人工智能技术的飞速发展,深度学习已成为推动创新的核心动力。本文将深入探讨在构建高效AI系统中,如何通过优化算法、调整网络结构及使用新型硬件资源等手段显著提升模型性能。我们将剖析先进的优化策略,如自适应学习率调整、梯度累积技巧以及正则化方法,并讨论其对模型训练稳定性和效率的影响。文中不仅提供理论分析,还结合实例说明如何在实际项目中应用这些优化技术。
|
1天前
|
移动开发 监控 Android开发
构建高效Android应用:Kotlin协程的实践与优化
【5月更文挑战第12天】 在移动开发领域,性能与响应性是衡量一个应用程序优劣的关键指标。特别是在Android平台上,由于设备的多样性和系统资源的限制,开发者需要精心编写代码以确保应用流畅运行。近年来,Kotlin语言因其简洁性和功能性而广受欢迎,尤其是其协程特性,为异步编程提供了强大而轻量级的解决方案。本文将深入探讨如何在Android应用中使用Kotlin协程来提升性能,以及如何针对实际问题进行优化,确保应用的高效稳定执行。
|
4天前
|
监控 供应链 数据可视化
深度解析BPM系统:优化业务流程,提升组织效率
本文探讨了业务流程管理系统(BPM)的核心价值和功能,以及低代码如何优化流程管理。BPM通过自动化和标准化流程,提高效率,降低技术复杂性,促进协作和监控。低代码平台加速了开发进程,增强了流程自动化,使得非专业开发者也能构建应用程序。结合低代码,企业能更轻松地适应市场变化,实现流程简化和业务增长。
8 1
|
13天前
|
缓存 监控 API
Android应用性能优化实践
【4月更文挑战第30天】 随着智能手机的普及,用户对移动应用的性能要求越来越高。对于Android开发者而言,提升应用的性能是吸引和保留用户的关键因素之一。本文将深入探讨影响Android应用性能的主要因素,并提供一系列的优化策略,旨在帮助开发者构建更加流畅和高效的应用体验。
|
13天前
|
移动开发 调度 Android开发
构建高效Android应用:Kotlin协程的实践与优化
【4月更文挑战第30天】在移动开发领域,性能和响应性是衡量应用质量的关键指标。对于Android平台而言,Kotlin协程作为一种新兴的异步编程解决方案,提供了更为简洁和高效的处理并发任务的能力。本文将深入探讨Kotlin协程的核心原理,以及如何通过它们来提升Android应用的性能。我们将从基础概念出发,逐步介绍协程的创建、管理以及与Android UI线程的交互,并最终展示如何优化现有代码以利用协程的优势。
|
13天前
|
前端开发 测试技术 数据处理
安卓开发中的MVP架构模式深度解析
【4月更文挑战第30天】在移动应用开发领域,模型-视图-呈现器(Model-View-Presenter, MVP)是一种广泛采用的架构模式。它旨在通过解耦组件间的直接交互来提高代码的可维护性和可测试性。本文将深入探讨MVP在安卓开发中的应用,揭示其如何促进代码的模块化,提升用户界面的响应性,并简化单元测试过程。我们将从理论概念出发,逐步过渡到实践案例,为读者提供一套行之有效的MVP实施策略。
|
14天前
|
移动开发 数据库 Android开发
构建高效Android应用:探究Kotlin协程的优化实践
【4月更文挑战第29天】在移动开发领域,尤其是Android平台上,性能优化一直是开发者关注的重点。近年来,Kotlin语言凭借其简洁性和功能性成为Android开发的热门选择。其中,Kotlin协程作为一种轻量级的并发处理机制,为编写异步代码、网络请求和数据库操作提供了极大的便利。本文将深入探讨Kotlin协程在Android应用中的性能优化技巧,帮助开发者构建更加高效的应用程序。
|
14天前
|
移动开发 API Android开发
Android应用性能优化实战
【4月更文挑战第28天】在移动开发领域,一个流畅的用户体验是至关重要的。对于Android开发者而言,应用的性能优化是一项既挑战性也极其重要的工作。本文将深入探讨Android应用性能优化的多个方面,包括内存管理、UI渲染、多线程处理以及电池效率等,旨在为开发者提供实用的性能提升策略和具体的实施步骤。通过分析常见的性能瓶颈,并结合最新的Android系统特性和工具,我们的目标是帮助读者打造更加高效、响应迅速的Android应用。
|
15天前
|
存储 缓存 网络协议
【专栏】理解并优化DNS设置对于提高网络速度至关重要
【4月更文挑战第28天】本文探讨了DNS服务器是否能加快网络访问速度。DNS负责将域名转换为IP地址,其查询时间、缓存机制和地理位置都影响网络速度。优化DNS配置,如选择快速的公共DNS服务、使用附近的服务器、确保设备正确配置和利用DNS缓存,都能有效提升网络体验。理解并优化DNS设置对于提高网络速度至关重要。

推荐镜像

更多