性能优化之App启动时间

App启动模式分类

1.冷启动
冷启动状态:系统不存在该应用的进程。启动应用才能创建出应用的进程。
一般是中应用在开机后或者系统停止后的第一次启动过程。因为系统和应用在冷启动时需要做跟多的工作
所以减少它的启动时间是最难的。建议始终基于冷启动的假设进行优化,因为这样做同样提升了另两种启动状态的表现。
冷启动初始时,系统完成三个任务

  • 启动和加载应用

  • 创建应用的专属进程

  • 启动后立刻显示启动的视图

    一旦系统穿件应用的专属进程,该进程开始创建应用:

  • 创建应用对象

  • 启动主线程

  • 创建MainActivity

  • 加载视图(inflating views)

  • 渲染布局(layout)

  • 渲染布局(layout)

  • .渲染布局(layout)

  • 执行初始化绘制

    启动流程大致如下:

       点击Launcher 上的 icon开加载app -->立即显示白屏或黑屏等 --> Application onCreate --> Activity Init---->
       Activity onCreate ---> 初始化数据,填充显示View ---> Activity onResume等
    

    2.暖启动

    在暖启动中,系统只需要把 Activity 切换到前台运行。如果应用的该 Activity 之前驻留在内存中,
    那么应用程序就不用重新初始化对象和渲染布局,但是,如果由于响应了低内存事件,
    例如在 onTrimMemory() 方法中清除了资源对象,那么这些对象就需要在热启动时重新创建
    处于冷启动与热启动之间,既包含一些冷启动的操作,又含有部分热启动的功能。例如以下两种状态:

    用户退出APP后重新Launch。此时此APP的进程可能会存在,然而,Activity 必须重新创建并调用onCreate方法
    APP 被缓存中清理掉时。此时用户重新Launch APP时,此app的进程和Activity都需要重新创建,但是任务栈中会保存部分APP实例数据(bundle类型)传递个Activity onCreate方法

    3.热启动
    热启动为冷启动的过程操作的子集,内存开销也更低。以下这些情况可以认为是热启动:
    1.用户退出应用,但随后重新启动它。应用的进程还在运行,但应用必须重新从 onCreate() 开始创建 Activity。
    2.系统从内存中清除了应用(非用户主动),然后用户重新启动它。进程和 Activity 需要重新启动,但 onCreate() 将接收到保存状态的 Bundle。
    事实上,savedInstanceState 在用户未主动销毁 Activity 时系统就会调用。

    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()

App启动时间测量与分析

1.通过adb命令测量App冷启动时间

   adb shell am start -W [packageName]/[packageName.MainActivity]
       或
       adb [-d|-e|-s <serialNumber>] shell am start -S -W [packageName]/[packageName.MainActivity] -c android.intent.category.LAUNCHER -a android.intent.action.MAIN
	  
	   例子如下:
	   adb shell am start -W com.example.administrator.androidtestdemo/com.example.administrator.BluetoothBLE.BlueToothActivity

2.在代码中测量app启动性能的方法

 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            Trace.beginSection("programandroid");
        }
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_blue_tooth);
        ButterKnife.bind(this);
        init();
        Configuration config = getResources().getConfiguration();
        int smallestScreenWidth = config.smallestScreenWidthDp;
        Log.i("BlueToothActivity","smallest width : "+ smallestScreenWidth);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
            Trace.endSection();
        }
		if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            reportFullyDrawn();
        }
		日志:
		09-19 14:54:06.456 1504-3591/? I/ActivityManager: Displayed com.example.administrator.androidtestdemo/com.example.administrator.BluetoothBLE.BlueToothActivity: +2s273ms
        Fully drawn com.example.administrator.androidtestdemo/com.example.administrator.BluetoothBLE.BlueToothActivity: +2s273ms

结合上述分析的我们优化的入口是应用和Activity对象的初始化及View展示
1.Applicatyion初始化开销大。
Application的创建过程。如果执行复杂逻辑或者大量对象被初始化,将会影响app启动时间
具体而言,就是你继承了 Application 并在初始化时执行了不必要的代码,比如:初始化 MainActivity 的状态信息;
创建了大量临时变量导致 GC(GC 在 ART 下影响很小);执行磁盘 I/O 操作(这甚至就会直接阻塞应用的执行);
反序列化操作;多重循环等等。

懒加载:只初始化那些必要的对象,而其他的全局静态对象移动到一个单例模式中。此外,可以考虑依赖注入框架 Dagger2 来创建对象及其依赖关系。

2.Activity 初始化开销大
Activity 的创建中除了要避免 Application 创建中提到的问题,还需要注意以下问题:
* 加载极其复杂的布局
* 主线程中出现磁盘或网络 I/O
* 加载和解码 Bitmap
* 渲染多个 VectorDrawable 对象。
解决问题的方法
1.视图层次过深:
减少冗余、嵌套的布局层次。
不布局绘制不可见的 UI,而是使用 ViewStub 对象在适当的时间布局绘制。
2.大量的资源初始化:
调整资源初始化的位置,可以在不同的线程执行懒加载。
加载部分视图,然后再加载大的位图和其他资源。

3.启动界面

     应用启动时就会立即显示启动界面。而通常是个白屏。可以设置与应用类似的启动动画。
	 这样可以向用户隐藏这个启动过程,用户会感受到应用已经在运行了。显示的界面就是应用的一部分或者流程的一部分。
  使用 Activity 的 windowBackground 属性,在启动时显示简单的自定义的画面。
		  
		  创建启动时显示的画面
		  <?xml version="1.0" encoding="utf-8"?>
		<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

			<item android:drawable="@color/colorAccent"/>
			<item>
				<bitmap android:src="@drawable/leak_canary_icon"
					android:gravity="center"/>
			</item>


		</layer-list>
		
		为启动的Activity自定义一个Theme
		<style name="AppTheme.Launcher">
			<item name="android:windowBackground">@drawable/start_activity_background</item>
		</style>
		
		在 AndroidManifest 文件中 Activity 的属性里设置该 style:
		 <activity android:name="com.example.administrator.BluetoothBLE.BlueToothActivity"
            android:theme="@style/AppTheme.Launcher">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
		
		这样子在应用启动时显示的画面就是你的自定义的画面了。但进入 Activity 后要正确的设置回正确的 style
		public class BlueToothActivity extends AppCompatActivity {
		  @Override
		  protected void onCreate(Bundle savedInstanceState) {
			// Make sure this is before calling super.onCreate
			setTheme(R.style.Theme_MyApp);
			super.onCreate(savedInstanceState);
			// ...
		  }
		}

4.利用TraceView分析启动时间


			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文件,

5.启动时间耗时常见原因及优化建议

	  1. 常见主要问题(持续补充ing)

                  部分数据库及IO的操作发生在首屏Activity主线程;
		Application中创建了线程池;
		启动时做密集沉重的初始化(Heavy app initialization);
		Multidex的使用,也是拖慢启动速度的元凶;
		UI存在过度绘制;
		首屏Activity网络请求密集;
		非核心功能资源过早请求加载
		工作线程使用未设置优先级;
		信息未缓存,重复获取同样信息;
		流程问题:例如闪屏图每次下载,当次使用;
		执行无用老代码;
		执行开发阶段使用的代码;
		执行重复逻辑;
		调用三方SDK里或者Demo里的多余代码;

	 2.建议:

		去掉无用但被执行的老代码;
		去掉开发阶段使用但线上被执行的代码;
		去掉重复逻辑执行代码;
		UI渲染优化,去除重复绘制,减少UI重复绘制时间
		去掉调用三方SDK里或者Demo里的多余代码;
		信息缓存,常用信息只在第一次获取,之后从缓存中取;
		项目是多进程架构,只在主进程执行Application的onCreate();
		根据优先级的划分,一些初始化工作能否将任务优先级划分成3个等级,任务优先级为2,3的,通过懒加载的方式在首页渲染完成后进行加载
		避免I/O操作、反序列化、网络操作、布局嵌套等。
		在Application的构造器方法、attachBaseContext()、onCreate()方法中不要进行耗时操作的初始化,一些数据预取放在异步线程中,可以采取Callable实现。
        对于sp的初始化,因为sp的特性在初始化时候会对数据全部读出来存在内存中,所以这个初始化放在主线程中不合适,反而会延迟应用的启动速度,对于这个还是需要放在异步线程中处理。
        对于MainActivity,由于在获取到第一帧前,需要对contentView进行测量布局绘制操作,尽量减少布局的层次,考虑StubView的延迟加载策略,当然在onCreate、onStart、onResume方法中避免做耗时操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值