通常来说, 一个App启动也会分如下二中不同的状态:
1.)冷启动
当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用, 这个启动方式就是冷启动。冷启动因为系统会重新创建一个新的进程分配给它,所以会先创 建和初始化Application类,再创建和初始化MainActivity类(包括一系列的测量、布局、绘 制),最后显示在界面上。
2.)热启动
当启动应用时,后台已有该应用的进程(例:按back键、home键,应用虽然会退出,但是该 应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种 启动会从已有的进程中来启动应用,这个方式叫热启动。热启动因为会从已有的进程中来启 动,所以热启动就不会走Application这步了,而是直接走MainActivity(包括一系列的测量、 布局、绘制),所以热启动的过程只需要创建和初始化一个MainActivity就行了,而不必创建和初始化Application,因为一个应用从新进程的创建到进程的销毁,Application只会初始化一次。
关于Activity启动时间的定义:
对于Activity来说,启动时,首先执行的是onCreate()、onStart()、onResume()这些生命周期函 数,但即使这些生命周期方法回调结束了,应用也不算已经完全启动,还需要等View树全部 构建完毕,一般认为,setContentView中的View全部显示结束了,算作是应用完全启动了。
Display Time
从API19之后,Android在系统Log中增加了Display的Log信息,通过过滤ActivityManager以及 Display这两个关键字,可以找到系统中的这个Log:
$ adb logcat | grep “ActivityManager”
ActivityManager:Displayed com.example.launcher/.LauncherActivity:+999ms
抓到的Log如图所示:
那么这个时间,实际上是Activity启动,到Layout全部显示的过程,但是要注意,这里并不包 括数据的加载,因为很多App在加载时会使用懒加载模式,即数据拉取后,再刷新默认的UI。
Application
Application是程序的主入口,特别是很多第三方SDK都会需要在Application的onCreate里面做 很多初始化操作,一般来说我们可以将这些初始化放在一个单独的线程中处理, 为了方便今后 管理,
优化的方法,无非是通过以下几个方面:
1)异步初始化 2)后台任务 3)界面预加载
异步初始化
这个很简单,就是让App在onCreate里面尽可能的少做事情,而利用手机的多核特性,尽可能 的利用多线程,例如一些第三方框架的初始化,如果能放线程,就尽量的放入线程中,最简 单的,你可以直接new Thread(),当然,你也可以通过公共的线程池来进行异步的初始化工 作,这个是最能够压缩启动时间的方式
后台任务
因为这个App一般会集成很多三方SDK等服务, 所以Application的onCreate有很多第三方平台的 初始化工作…
Application代码如下:
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
LitePal.initialize(this.getApplicationContext());
} }
使用IntentService不同于Service, 它是工作在后台线程的.
IntentService代码如下:
public class InitializeService extends IntentService {
private static final String ACTION_INIT_WHEN_APP_CREATE = "com.maweiqi";
public InitializeService(String name) {
super(name);
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_INIT_WHEN_APP_CREATE.equals(action)) {
performInit();
}
}
}
private void performInit() {
LitePal.initialize(this.getApplicationContext());
}
public static void start(Context context) {
Intent intent = new Intent(context, InitializeService.class);
intent.setAction(ACTION_INIT_WHEN_APP_CREATE);
context.startService(intent);
}
}
MyApp的onCreate改成:
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
InitializeService.start(this);
}
}
最终的效果
界面预加载
当系统加载一个Activity的时候,onCreate()是一个耗时过程,那么在这个过程中,系统为了让 用户能有一个比较好的体验,实际上会先绘制一些初始界面,类似于PlaceHolder。
系统首先会读取当前Activity的Theme,然后根据Theme中的配置来绘制,当Activity加载完毕 后,才会替换为真正的界面。所以,Google官方提供的解决方案,就是通过 android:windowBackground属性,来进行加载前的配置,同时,这里不仅可以配置颜色,还能 配置图片,例如,我们可以使用一个layer-list来作为android:windowBackground要显示的图:
在drawable文件夹下面 , 做一个logo_splash的背景: start_window.xml
-
在style里面弄一个主题:
在清单文件里面给Activity指定需要预加载的Style:
activity代码如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
首屏Activity的渲染
Android系统每隔16ms会发出VSYNC信号重绘我们的界面(Activity). 为什么是16ms, 因为 Android设定的刷新率是60FPS(Frame Per Second), 也就是每秒60帧的刷新率, 约合16ms刷新一 次.就像是这样的:
这就意味着, 我们需要在16ms内完成下一次要刷新的界面的相关运算, 以便界面刷新更新. 然而, 如果我们无法在16ms内完成此次运算会怎样呢?
例如, 假设我们更新屏幕的背景图片, 需要24ms来做这次运算. 当系统在第一个16ms时刷新界 面, 然而我们的运算还没有结束, 无法绘出图片. 当系统隔16ms再发一次VSYNC信息重绘界面 时, 用户才会看到更新后的图片. 也就是说用户是32ms后看到了这次刷新(注意, 并不是24ms). 这就是传说中的丢帧(dropped frame):
丢帧给用户的感觉就是卡顿, 而且如果运算过于复杂, 丢帧会更多, 导致界面常常处于停滞状态, 卡到爆.