美文网首页iOS 功能类
App启动优化(三)启动优化方案

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

作者: Geekholt | 来源:发表于2019-12-24 14:11 被阅读0次

系列文章

目录

前言

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

视觉优化

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

解决方案

  1. 新增一个主题样式,设置windowBackground属性,在其中放一张背景图片,或是广告图片之类的
<style name="AppTheme.Launcher" parent="Theme.AppCompat.Light.NoActionBar">
     <item name="android:windowBackground">@drawable/bg_launcher</item
</style>

bg_launcher.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <bitmap
            android:gravity="center"
            android:src="@drawable/ic_android" />
    </item>
</layer-list>
  1. MainActivity设置主题
<activity android:name=".MainActivity"
    android:theme="@style/AppTheme.Launcher">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />

        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
  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启动优化(三)启动优化方案

    系列文章 App启动优化(一)冷启动和热启动 App启动优化(二)启动时间测量 App启动优化(三)启动优化方案 ...

  • App白屏和启动优化的一些思路

    App启动优化 App启动优化原理与技术方案 启动优化 黑白屏问题 启动页面主题设置为图片 启动页面,不要再onC...

  • 性能优化:App启动优化

    一、App启动流程及启动优化二、定量监测App启动耗时、定位耗时代码三、果速送App启动优化 一、App启动流程及...

  • Android性能优化----APP启动优化

    现实开发中用到的性能优化方案 归纳如下: 1.APP的启动速度 想要对app的启动速度优化,就必须熟悉其启动流...

  • iOS 性能优化三

    主要讲解APP冷启动的优化 iOS 性能优化一iOS 性能优化二iOS 性能优化三 1. APP 启动的分类 冷...

  • Android性能优化之启动优化(实战篇)

    目录 一、启动优化的意义 二、启动时间检测 三、启动优化工具---traceview 四、优化方案1.异步初始化2...

  • 面试题:APP启动优化

    一、APP启动分为冷启动和热启动 二、APP冷启动三大阶段 三、APP启动优化 四、共享缓存机制

  • 50. 安卓启动优化

    启动优化包含app的启动和单页面的启动,今天只说app的启动,二者优化的逻辑是相同的。 app启动的三种状态 冷启...

  • 如何优化 App 的启动时间

    如何优化 App 的启动时间 如何优化 App 的启动时间

  • APP性能优化

    一、APP启动性能优化。 APP启动主要分冷启动和热启动,主要优化冷启动。 1.尽量减少didFinishLauc...

网友评论

    本文标题:App启动优化(三)启动优化方案

    本文链接:https://www.haomeiwen.com/subject/hdxfoctx.html