一、启动定义

启动:从手指点击app图标,到第一个页面加载完成.


二、启动类型


在安卓中应用的启动方式分为以下几种:


1.冷启动


冷启动:当启动应用时,后台没有该应用的进程,系统会创建一个新的进程分配给该应用。

冷启动会先创建和初始化 Application 类,再创建和初始化 MainActivity 类,最后显示在界面上。


2 .热启动

热启动:当启动应用时,后台已有该应用的进程(如:按back键、home键)。

热启动只会创建和初始化 MainActivity 类,不会创建和初始化 Application,因为Application 只会初始化一次。


3 .首次启动


首次启动:安装应用后第一次启动的过程。

首次启动也是冷启动,比起冷启动,还会做一些系统初始化工作,如缓存目录创建、数据库创建,所以首次启动花费的时间最长。首次启动的速度非常重要,毕竟影响用户对 App 的第一映像。


三、启动流程


1.创建流程


  1. 点击应用图标后,进入Launcher的onClick(), 然后调用 startActivity (),然后调用 Instrumentation 的 execStartActivity()。
  2. Instrumentation 调用 ActivityManagerProxy (ActivityManagerService 在应用进程的一个代理对象) 的 startActivity ()。
  3. ActivityManagerProxy 跨进程调用 ActivityManagerService (运行在 system_server 进程)的 startActivity ()。
  4. ActivityManagerService 最后调用 zygoteSendArgsAndGetResult(), 通过 socket 发送给 zygote 进程,zygote 进程会孵化出新的应用进程。
  5. zygote 执行 ActivityThread 的 main()。在该方法里会先准备好 Looper 和消息队列,然后调用 attach 方法将应用进程绑定到 ActivityManagerService,然后进入 loop 循环,不断地读取消息队列里的消息,并分发消息。
  6. ActivityManagerService 保存应用进程的一个代理对象,然后 ActivityManagerService 通过代理对象通知应用进程创建入口 Activity 的实例,并执行它的生命周期函数。


2.生命周期


创建应用进程之前的流程由系统决定,没办法优化。能够优化的是Application和MainActivity 的创建和初始化。

下面是 MainActivity 的启动流程:



-> Application 构造函数



-> Application.attachBaseContext()

-> Application.onCreate()


-> Activity.setTheme()

-> Activity 构造函数 -> Activity.onCreate()


-> Activity.onAttachedToWindow

-> Activity.onStart -> Activity.onResume


-> Activity.onWindowFocusChanged



四、启动时间


1.Activity启动时间统计


通过命令行方式统计启动时间:

adb shell am start -W appplicationId/packageName.activityName

输出的结果类似于:



$ adb shell am start -W com.speed.test/com.speed.test.HomeActivity



Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.speed.test/.HomeActivity }

Status: ok Activity: com.speed.test/.HomeActivity ThisTime: 496 TotalTime: 496 WaitTime: 503


Complete



  • ThisTime 最后一个Activity的启动耗时;
  • TotalTime 所有Activity(指定的Activity可能启动其他Activity)的启动耗时;。
  • WaitTime 调用startActivity() 到最后一个Activity启动完成的时间,即当前Activity的onPause()时间+TotalTime


2.精确时间统计


通过trace文件分析每个方法的花费时间.

1.生成trace文件
  1. 使用代码生成
    先通过Debug类生成trace文件,注意加读写权限。

Debug.startMethodTracing("shixintrace");

  1. ...

Debug.stopMethodTracing();

注意:在Applicaiton中开始trace,然后再MainActivity中结束trace,不会生成trace文件。

trace文件生成后存放在SD卡中,可能直接存放/sdcard,也可能存在外置专有目录中。

先通过以下命令找到具体路径:

adb shell

  1. cd /sdcard

find -name *.trace

  1. 可能得到如下结果

./Android/data/gsw.test1/files/shixintrace.trace

然后退出shell状态,通过adb命令取出文件

adb pull sdcard/Android/data/gsw.test1/files/shixintrace.trace
  1. 使用 Android Studio生成
    生成 trace 文件:Android Monitor -CPU监控-闹钟。
2.分析trace文件

如果使用代码生成trace文件,则需要用Android Studio打开.

分析trace文件:

  1. 先分析线程的执行时间,对时间较长的线程,再分析每个方法的执行时间和调用次数。
  2. 对于执行时间长的方法,需要分析每一步的执行时间,知道找到最深层的长耗时方法。
  3. 对于调用频繁的方法,需要逐步逐层分析调用和依赖。


五、优化方案


1.减少启动耗时


  1. 在Application的构造器方法、attachBaseContext()、onCreate()方法中不要进行耗时操作的初始化,一些数据预取放在异步线程中,可以采取Callable实现。
  2. 对于sp(首选项)的初始化,因为sp的特性在初始化时候会对数据全部读出来存在内存中,所以这个初始化放在主线程中不合适,反而会延迟应用的启动速度,对于这个还是需要放在异步线程中处理。
  3. 对于MainActivity,由于在获取到第一帧前,需要对contentView进行测量布局绘制操作,尽量减少布局的层次,考虑StubView的延迟加载策略,当然在onCreate、onStart、onResume方法中避免做耗时操作。


2.黑白屏优化


  1. 设置特定的背景
    通过设置theme的android:windowBackground属性实现。
    如果设置的背景在应用启动完成后不再需要,可以再通过setTheme()设置背景。
  2. 不显示背景
    通过设置theme的android:windowIsTranslucent属性实现:true代表透明;false代表非透明。