应用的启动速度缓慢是我们在开发过程中常常会遇到的问题,比方启动缓慢导致的黑屏。白屏问题,本篇博客就将介绍App启动优化的相关知识。
应用的启动方式
通常来说,启动方式分为两种:冷启动和热启动。
1、冷启动:当启动应用时。后台没有该应用的进程。这时系统会又一次创建一个新的进程分配给该应用。这个启动方式就是冷启动。
冷启动由于系统会又一次创建一个新的进程分配给它。所以会先创建和初始化Application类,再创建和初始化MainActivity类(包含一系列的測量、布局、绘制),最后显示在界面上。
2、热启动:当启动应用时,后台已有该应用的进程(例:按back键、home键,应用尽管会退出,可是该应用的进程是依旧会保留在后台,可进入任务列表查看)。所以在已有进程的情况下,这样的启动会从已有的进程中来启动应用,这个方式叫热启动。热启动由于会从已有的进程中来启动,所以热启动就不会走Application这步了。而是直接走MainActivity(包含一系列的測量、布局、绘制),所以热启动的过程仅仅须要创建和初始化一个MainActivity即可了,而不必创建和初始化Application,由于一个应用从新进程的创建到进程的销毁,Application仅仅会初始化一次。
App的启动过程
本文所指的优化针对冷启动。简单解释一下App的启动过程:
1.点击Launcher。启动程序,通知ActivityManagerService
2.ActivityManagerService通知zygote进程孵化出应用进程,分配内存空间等
3.运行该应用ActivityThread的main()方法
4.应用程序通知ActivityManagerService它已经启动,ActivityManagerService保存一个该应用的代理对象,ActivityManagerService通过它能够控制应用进程
5.ActivityManagerService通知应用进程创建入口的Activity实例,运行它的生命周期
启动过程中Application和入口Activity的生命周期方法按例如以下顺序调用:
1.Application 构造方法
2.attachBaseContext()
3.onCreate()
4.入口Activity的对象构造
5.setTheme() 设置主题等信息
6.入口Activity的onCreate()
7.入口Activity的onStart()
8.入口Activity的onResume()
9.入口Activity的onAttachToWindow()
10.入口Activity的onWindowFocusChanged()
什么才是应用的启动时间
从点击应用的启动图标開始创建出一个新的进程直到我们看到了界面的第一帧。这段时间就是应用的启动时间。
我们要測量的也就是这段时间,測量这段时间能够通过adb shell命令的方式进行測量,这样的方法測量的最为精确,命令为:
adb shell am start -W [PackageName]/[PackageName.MainActivity]
1、ThisTime:一般和TotalTime时间一样。除非在应用启动时开了一个透明的Activity预先处理一些事再显示出主Activity,这样将比TotalTime小。
2、TotalTime:应用的启动时间,包含创建进程+Application初始化+Activity初始化到界面显示。
3、WaitTime:一般比TotalTime大点,包含系统影响的耗时。
利用TraceView分析启动时间
在onCreate開始和结尾打上trace.
Debug.startMethodTracing("TestApp");
...
Debug.stopMethodTracing();
运行程序, 会在sdcard上生成一个”TestApp.trace”的文件.
注意: 须要给程序加上写存储的权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
通过adb pull将其导出到本地
adb pull /sdcard/TestApp.trace ~/testSpeed.trace
打开DDMS分析trace文件,会出现下面的界面
一般仅仅须要关注:Calls + Recur Calls / Total和 Cpu Time / Call
Cpu Time / Call反映调用次数不多,但每次调用却须要花费非常长时间的函数
Calls + Recur Calls / Total反映自身占用时间不长,但调用却非常频繁的函数
怎样降低应用启动时的耗时
针对冷启动时候的一些耗时。能够採取下面策略:
1、在Application的构造器方法、attachBaseContext()、onCreate()方法中不要进行耗时操作的初始化,一些数据预取放在异步线程中,能够採取Callable实现。
2、对于sp的初始化,由于sp的特性在初始化时候会对数据所有读出来存在内存中,所以这个初始化放在主线程中不合适,反而会延迟应用的启动速度。对于这个还是须要放在异步线程中处理。
3、对于MainActivity,由于在获取到第一帧前。须要对contentView进行測量布局绘制操作,尽量降低布局的层次。考虑StubView的延迟载入策略,当然在onCreate、onStart、onResume方法中避免做耗时操作。
遵循上面三种策略可明显提高app启动速度。
优化应用启动时的体验
对于应用的启动时间,仅仅能是尽量的避免一些耗时的、非必要的操作在主线程中。这样相对能够缩减一部分启动的耗时。另外一方面在等待第一帧显示的时间里,能够增加一些配置以增加体验,比方增加Activity的background,这个背景会在显示第一帧前提前显示在界面上。 对于应用的启动时间,仅仅能是尽量的避免一些耗时的、非必要的操作在主线程中,这样相对能够缩减一部分启动的耗时,另外一方面在等待第一帧显示的时间里,能够增加一些配置以增加体验。比方增加Activity的background,这个背景会在显示第一帧前提前显示在界面上。
方案1:
1、先为主界面单独写一个主题style,设置一张待显示的图片,这里我设置了一个颜色,然后在manifest中设置给MainActivity:
<style name="AppTheme.Launcher">
<item name="android:windowBackground">@drawable/bule</item>
</style>
//...
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.Launcher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
2、然后在MainActivity中载入布局前把AppTheme又一次设置给MainActivity:
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
这样在启动时会先显示background,然后待界面绘制完毕再显示主界面:
方案2:通过设置Style
(1)设置背景图Theme
通过设置一张背景图。
当程序启动时。首先显示这张背景图。避免出现黑屏
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:screenOrientation">portrait</item>
<item name="android:windowBackground">>@mipmap/splash</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
</style>
(2)设置透明Theme
通过把样式设置为透明,程序启动后不会黑屏而是整个透明了,等到界面初始化完才一次性显示出来
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:screenOrientation">portrait</item>
</style>
两者对照:
Theme1 程序启动快。界面先显示背景图,然后再刷新其它界面控件。给人刷新不同步感觉。
Theme2 给人程序启动慢感觉。界面一次性刷出来。刷新同步。
(3)改动AndroidManifest.xml
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true">
<activity android:name=".MainActivity"
android:theme="@style/AppTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
//......
</application>