安卓性能优化(1)之冷启动 热启动详解

应用启动方式

通常来说,启动方式分为两种:冷启动和热启动。

  1、冷启动:当启动应用时,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用,这个启动方式就是冷启动。

  2、热启动:当启动应用时,后台已有该应用的进程(例:按back键、home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),所以在已有进程的情况下,这种启动会从已有的进程中来启动应用,这个方式叫热启动。

  特点

  1、冷启动:冷启动因为系统会重新创建一个新的进程分配给它,所以会先创建和初始化Application类,再创建和初始化第一个Activity类(包括一系列的测量、布局、绘制,当然了,有一种特殊的情况,启动activity可以不需要测量布局绘制,这个后面再说),最后显示在界面上。

  2、热启动:热启动因为会从已有的进程中来启动,所以热启动就不会走Application这步了,而是直接走第一个activty(包括一系列的测量、布局、绘制),所以热启动的过程只需要创建和初始化第一个activty就行了,而不必创建和初始化Application。

  因为一个应用从新进程的创建到进程的销毁,Application只会初始化一次。

应用的启动过程

冷启动启动流程:当点击app的启动图标时,安卓系统会从Zygote进程中fork创建出一个新的进程分配给该应用,之后会依次创建和初始化Application类、创建第一个activity、加载主题样式Theme中的windowBackground等属性设置给第一个activity以及配置Activity层级上的一些属性、再inflate布局、当onCreate/onStart/onResume方法都走完了后最后才进行contentView的measure/layout/draw显示在界面上,所以直到这里,应用的第一次启动才算完成,这时候我们看到的界面也就是所说的第一帧。所以,总结一下,应用的启动流程如下:

  Application的构造器方法——>attachBaseContext()——>onCreate()——>Activity的构造方法——>onCreate()——>配置主题中背景等属性——>onStart()——>onResume()——>测量布局绘制显示在界面上。

  大致流程如下:

  1、点击桌面图标,Launcher会启动程序默认的Acticity,之后再按照程序的逻辑启动各种Activity。

  2、启动Activity都需要借助应用程序框架层的ActivityManagerService服务进程(Service也是由ActivityManagerService进程来启动的);在Android应用程序框架层中,ActivityManagerService是一个非常重要的接口,它不但负责启动Activity和Service,还负责管理Activity和Service。

  Step 1. 无论是通过Launcher来启动Activity,还是通过Activity内部调用startActivity接口来启动新的Activity,都通过Binder进程间通信进入到ActivityManagerService进程中,并且调用ActivityManagerService.startActivity接口;

  Step 2. ActivityManagerService调用ActivityStack.startActivityMayWait来做准备要启动的Activity的相关信息;

  Step 3. ActivityStack通知ApplicationThread要进行Activity启动调度了,这里的ApplicationThread代表的是调用ActivityManagerService.startActivity接口的进程,对于通过点击应用程序图标的情景来说,这个进程就是Launcher了,而对于通过在Activity内部调用startActivity的情景来说,这个进程就是这个Activity所在的进程了;

  Step 4. ApplicationThread不执行真正的启动操作,它通过调用ActivityManagerService.activityPaused接口进入到ActivityManagerService进程中,看看是否需要创建新的进程来启动Activity;

  Step 5. 对于通过点击应用程序图标来启动Activity的情景来说,ActivityManagerService在这一步中,会调用startProcessLocked来创建一个新的进程,而对于通过在Activity内部调用startActivity来启动新的Activity来说,这一步是不需要执行的,因为新的Activity就在原来的Activity所在的进程中进行启动;

  Step 6. ActivityManagerServic调用ApplicationThread.scheduleLaunchActivity接口,通知相应的进程执行启动Activity的操作;

  Step 7. ApplicationThread把这个启动Activity的操作转发给ActivityThread,ActivityThread通过ClassLoader导入相应的Activity类,然后把它启动起来。

计算冷启动时间

误区: 各种博客帖子上有两种方式获取冷启动时间:
1. 在第一个启动的activity的onCreate()方法添加reportFullyDrawn()方法:

try {
   if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
           reportFullyDrawn();
    }
 }catch (SecurityException e) {
        e.printStackTrace();
    }

2.使用命令行:

adb shell am start -S -W packagename/yourActivityPath.LaunchActivity 

经本人测试,两种方式测试下来,其实都是不对的,都是测试的指定activity的启动过程。而app的冷启动过程,应该是从Application的构造方法开始,然后是Application的attachBaseContext(),onCreate()。正确的测试冷启动时间的方法,应该是在Application的attachBaseContext()和第一个actvity的onResume()添加时间log,计算出两者时间之差:

Log.e("time", "time1-->" + System.currentTimeMillis());

冷启动过程中碰到的白屏黑屏问题

未完全解决问题的方案

网上很多说是在style文件中添加如下属性:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> 
<item name="android:windowIsTranslucent">true</item> 
<item name="android:windowNoTitle">true</item> 
</style>

就是将主题设置为透明,这样用户点击应用的icon之后,就将系统默认的白屏主题替换为透明色。
然并卵,如果app冷启动时间比较长,用户点击应用icon之后,会有较长一段时间看不到app的启动,也是得不偿失的。

有效解决方案

将app的第一个actvity,即LaunchActivity的主题设置如下:

 <activity  android:name=".LaunchActivity" android:screenOrientation="portrait" android:theme="@style/AppTheme.Splash">
     <intent-filter>
         <action android:name="android.intent.action.MAIN" />

         <category android:name="android.intent.category.LAUNCHER" />
     </intent-filter>
</activity>

其中AppTheme.Splash属性设置如下:

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:windowIsTranslucent">false</item>
        <item name="android:textColorPrimary">@color/white</item>
        <item name="android:screenOrientation">portrait</item>
        <item name="android:splitMotionEvents">false</item>
    </style>

    <style name="AppTheme.Splash" parent="AppTheme">
        <item name="android:windowBackground">@drawable/app_splash</item>
        <item name="android:windowFullscreen">true</item>
    </style>

app_splash就是你app想设置的splash启动图。而在你的第一个actvity里,也并不需要setContentView(),只需要在onCreate()方法中添加后续的进入其他activity的逻辑。这样的话,当Application初始化完成后,以及在Application的attachBaseContext()和onCreate()进行一系列的应用初始化动作之后,再进入到第一个activity。这样既解决了白屏问题,又节省了冷启动时间(第一个activity不需要测量布局绘制view)。

冷启动其他的优化

  1. 减少在Application和第一个Activity的onCreate()方法的工作量;
  2. 不要让Application参与业务的操作;
  3. 不要以静态变量的方式在Application中保存数据;
  4. 在Application中进行初始化时,能将初始化放到子线程,就尽量放到子线程,以避免主线程阻塞时间太长。
    原文作者:罗小辉
    原文地址: https://blog.csdn.net/a394268045/article/details/81557484
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞