App启动优化(三)启动优化方案

如需转载请评论或简信,并注明出处,未经允许不得转载

系列文章

  • App启动优化(一)冷启动和热启动
  • App启动优化(二)启动时间测量
  • App启动优化(三)启动优化方案

目录

前言

前面的章节我们已经介绍了启动分类的启动流程,同时也介绍了启动时间的测量方式。接下来将介绍两种启动优化方式 — 视觉优化和异步优化

视觉优化

app启动后,WindowManager会先加载app theme中的windowBackground,所以通常就会出现白屏的情况(取决于你的主题是Dark还是Light),我们对windowBackground进行设置,让用户从视觉上感觉app的启动变快

解决方案

  1. 新增一个主题样式,设置windowBackground属性,在其中放一张背景图片,或是广告图片之类的

bg_launcher.xml



    
        
    

  1. MainActivity设置主题

    
        

        
    

  1. MainActivityonCreate()方法执行前将主题替换回原来的样式
@Override
protected void onCreate(Bundle savedInstanceState) {
    //替换为原来的主题,在onCreate之前调用
    setTheme(R.style.AppTheme);
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

这种方式虽然不能加快应用的启动时间,但是可以防止应用启动白屏,带来更好的用户体验

异步优化

如何进行异步优化

核心思想:子线程分担主线程任务,通过并行减少时间

异步优化.png

异步优化的思想其实很明确,也很容易理解,但是真正在异步优化的实践过程中,其实是有很多“坑”的

这里写了一个简单的demo,我们在Application中进行了一些初始化操作,实际场景往往比这要复杂很多

Application.java

@Override
public void onCreate() {
    super.onCreate();
    //高德地图
    initAMap();

    //weex
    initWeex();

    //Stecho
    initStetho();

    //图片加载
    initFresco();

    //自己写的代码
    initDeviceId();

    //推送
    initJPush();
}

显然,上面这种初始化方式是串行的,如果初始化的内容过多,显然启动速度就会变慢。结合我们说的异步初始化的思想,我们使用了线程池来来优化我们的初始化,使得初始化代码能够并行

这里的CORE_POOL_SIZE我们参照了AsyncTask的源码,根据cpu数量来确定线程池的核心池的数量

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool, 
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));

@Override
public void onCreate() {
    super.onCreate();
    mApplication = this;
    ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE);
    service.submit(new Runnable() {
        @Override
        public void run() {
            //高德地图
            initAMap();
        }
    });

    service.submit(new Runnable() {
        @Override
        public void run() {
            //weex
            initWeex();
        }
    });
    
    service.submit(new Runnable() {
            @Override
            public void run() {
                //Stecho
                initStetho();
            }
        });
    
    ....
}

如果对线程池比较熟的人会知道,现在ApplicationonCreate()方法一定执行的特别快。但是有的人可能会问,这里我们能不能把所有初始化方法都放到一个Runnable中去呢?

这样做理论上其实是可以的,因为它是异步的,但是上面说了,我们这里其实是根据cpu数量来确定线程池的核心池的数量,假设创建出来3个线程,但是我们只用了一个,那这样显然是一种资源的浪费,所以我们采取了对每个初始化方法都进行了submit的方式

异步优化.png

我们可以看到,通过异步的方式进行初始化,效果是很明显的

异步优化的问题

见识到了异步优化的成果,但是先不要急着高兴,简单思考一下的话我们就会发现,这种方式其实是并不完全合理的,很多场景下我们实际上并不能直接使用这种方案,这里举几个例子

  1. 不符合异步要求。实际项目初始化往往比较复杂,有时候会遇到某些初始化方法一定要在主线程执行,那么用上面这种方式显然也是不可行的
  2. 需要在某阶段完成。例如我们在闪屏页面就要用到weex的相关方法,这时候如果weex在子线程还没有初始化成功,显然就会有问题
  3. 任务之间有依赖关系。例如我们这里的initJPush()方法,这个方法需要用到DeviceId,所以就需要在 initDeviceId()之后执行

针对问题1,我们需要修改我们的代码,把不符合异步要求的方法放到主线程执行

针对问题2,这里介绍一个小技巧,我们用到了CountDownLatch,构造方法中传入1,这里的意思是countDownLatch必须被满足一次,也就是说直到 countDownLatch.countDown()被执行,否则countDownLatch.await()会一直等待

    private CountDownLatch countDownLatch = new CountDownLatch(1); 

    @Override
    public void onCreate() {
        super.onCreate();
        mApplication = this;
        ExecutorService service = Executors.newFixedThreadPool(CORE_POOL_SIZE); 
        
        .....
        
        service.submit(new Runnable() {
            @Override
            public void run() {
                //weex
                initWeex();
                countDownLatch.countDown();
            }
        });

        .....

        try {
            countDownLatch.await();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

针对问题3,我们很容易想到下面这种方式,把initDeviceId()放到initJPush()之前

service.submit(new Runnable() {
    @Override
    public void run() {
        initDeviceId();
        initJPush();
    }
});

这样做的话,看起来好像解决了问题。但是实际场景往往很复杂,有很多类似的这种依赖关系,我们很可能遇到一个情况,就是我们并不能直接把某个初始化方法移到同一个Runnable

我们上面这种写法还有一个很大的问题,就是代码不优雅,如果实际项目中有一百个初始化项目呢?难道就要写一百次service.submit()方法,我相信很多同学肯定是无法接受的,因为不优雅的代码同时也带了很高的维护成本

总结

本文介绍了两个比较常规的启动优化方案,一个是视觉优化,一个是异步优化。但是经过本文的分析,我们看到了,常规的异步优化方法,在真实项目中实施起来,其实是存在很多问题的。那我们如何来解决这些问题呢?请看启动优化(四),直接项目奉上,有兴趣可以自己看看,具体分析可能要等到哪天有空我想分析的时候了。。

你可能感兴趣的:(App启动优化(三)启动优化方案)