应用启动过程从用户点击launcher图标到看到第一帧这个过程中,主要会经过以下这些过程:

main()->Application:attachBaseContext()->onCreate()->Activity:onCreate()->onStart()->onPostCreate()->onResume()->onPostResume()

而一般我们的初始化任务主要都会集中化在Application:onCreate()方法中,这就使得初始化任务在第一帧绘制之前得完成,这就造成了卡图标、应用启动慢。那么把任务打散呢?分散在LaunchActivity中去分段初始化?还是不行的,因为界面开始绘制是在onResume()方法开始后才开始绘制,所以,得从Activity的创建过程找办法。 main->Activity创建的这个过程会经过一系列framework层的操作,这些操作都是系统自动执行的,不易进行优化,不过可以在Activity创建这个过程前后来找一些蛛丝马迹,因为Activity的创建都会辗转到ActivityThread:performLaunchActivity()这个方法中,在这个方法中可以知道这么几件事:

  1. 先通过Instrumentation:newActivity()来创建一个Activity实例
  2. 再判断Application实例是否已创建,已创建则直接返回,否则调用 Instrumentation:newApplication()来创建Application实例,在这个过程中会依次执行attachBaseContext()和onCreate()方法
  3. 之后Activity:attach()方法会创建一个PhoneWindow对象,它就是界面,它有一个DecorView,调用setContentView()时会给配置DecorView,其中就会设置一个背景:
    if(getContainer==null){
    final Drawable background;
    if(mBackgroundResource!=0){
      background=getContext().getDrawable( mBackgroundResource );  //此处background来自theme中设置的windowbackgroud
    }else{
      background= mBackgroundResource;
    }
    mDecor.setWindowBackground( background );
    }
    

    我们的View也是add进DecorView中显示,它作为RootView肯定是最先显示,所以可以给它设置个默认背景

  4. 最后依次调用Activity的onCreate、onStart等方法

优化措施

  1. 任务分级
  2. 任务并行 (可以并行的次要任务可以放在子线程中执行)
  3. 界面预显示 (设置theme的windowbackground) 对于任务集中初始化化、耗时初始化原因导致应用在中低端机启动过慢,而Activity界面绘制的时机导致简单的将任务分给Activity初始化也不起作用,我们必须找一个切入点 界面的创建和界面的绘制,这两个过程第一个是Application的attachBaseConte和onCreate这两个方法影响的,第二个则是Application创建一直到界面绘制 所以,可以对任务进行分级的临界点可以这样分:
  4. CoreSDK——Application的创建
  5. HighPrioritySDK——Activity的创建
  6. LowPrioritySDK——Activity界面完成绘制
  7. AsyncSDK——Activity的创建

分级带来的问题 正常启动过程那肯定是没问题的,不过有这么几种场景:

  1. App切回后台,内存不足导致Application被回收,从最近任务列表中恢复界面时Application需重新创建
  2. 应用没挂起时,Push推送需从Notification跳入应用内某界面
  3. 应用没挂起时,浏览器外链需跳入应用内某界面 这些Case可能导致的问题是被跳入的界面使用到了未初始化的SDK,可能导致Crash或者数据异常,所以目标页面启动前必须确保SDK已经初始化,这个过程的原因是没有唤起启动页来初始化SDK,可以通过hook newActivity解决,其中判断Application是否初始化和各个SDK是否初始化,并且判断该次启动不来自系统的Launcher。
public class HookInstrumentation extends Instrumentation{
    @Override
    public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
        if (!intent.hasCategory(Intent.CATEGORY_LAUNCHER)){
            SdkUntil.initHighSDK();
            SdkUntil.initLowSDK();
            SdkUntil.initAsyncSDK();
        }
        return super.newActivity(cl, className, intent);
    }
}

通过反射将原来的Instrumentation hook掉

 public static void attachContext() throws Exception{
        // 先获取到当前的ActivityThread对象
        Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
        Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
        currentActivityThreadMethod.setAccessible(true);
        Object currentActivityThread = currentActivityThreadMethod.invoke(null);

        // 拿到原始的 mInstrumentation字段
        Field mInstrumentationField = activityThreadClass.getDeclaredField("mInstrumentation");
        mInstrumentationField.setAccessible(true);
        Instrumentation mInstrumentation = (Instrumentation) mInstrumentationField.get(currentActivityThread);

        // 创建代理对象
        Instrumentation hookInstrumentation = new HookInstrumentation();

        // 偷梁换柱
        mInstrumentationField.set(currentActivityThread, hookInstrumentation);
    }
public class SdkUntil {
    public static synchronized void initLowSDK(){
        if(InitializeOptimizer.isLowSDKInitialized()){ //sdk未初始化
            return;
        }
        InitializeOptimizer.setLowSDKInitialized(true);//设置标识位为true并初始化
        //做一些sdk初始化的操作
       }
       public static void initAsyncSDK(){
        if(InitializeOptimizer.isAsyncSDKInitialized()){
            return;
        }
        InitializeOptimizer.setAsyncSDKInitialized(true);
       //同上
    }
}