Android的冷启动优化

Android的冷启动优化

1、启动过程概述

在应用层,普通APP启动过程大致如下:

1.加载Application

静态代码块/构造函数
onCreate()方法

2.加载主Activity

静态代码段/构造函数
消息队列第一次循环:onCreate(),通过setContentView()解析、加载XML
消息队列第二次循环:被动的调用Choreographerd中的FrameDisplayEventRecevier的run()进行实际绘制
为了提高用户感知,我希望在主线程中执行的顺序如下(注意本流程不适用于插件化的App)
1.尽快显示DecoView(Main Thread)(显示Theme中定义的ActionBar、背景等)
2.尽快显示xml中的静态View(Main Thread)(显示xml的布局)
3.加载第三方黑盒SDK(Main Thread)
4.进行网络、图片等框架的构造(Main Thread)
5.通过框架进行业务请求(Gson/OkHttp等 Worker Thread),并更新View

不建议在Application中初始化耗时任务,它将直接导致白屏


2、用户感知优化

本部分可以提高上文1、2、3的用户体验

2.1加载伪背景(0.1~0.2s)

DecoView的优先级比setContentView的优先级高,所以可以让DecoView显示一个伪启动背景界面,而不是白屏黑屏或者没界面甩锅给手机厂商,让用户感受到App正在加载是一个好的选择。

绘制一个App启动的草图,如下,一个是Toolbar、一个是背景

<layer-list
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:opacity="opaque"
    />
<item android:gravity="top"
    <shape android:shape="rectangle">
        <solid android:color="#c8ececec"/>
    </shape>
</item>
<item
  android:top="75dp"
  android:gravity="top">
    <shape android:shape="rectangle">
     <solid android:color="@color/primary"/>
    </shape>
</item>
</layer-list>

设置windowBackground

<style name="ColdStartTheme" parent="APPTheme">
    <item name="android:windowBackground">@drawable/cold_start_bg
    </item>
</style>

在启动时先加载了伪背景,然后才加载了真正的View元素


最终可以让用户觉得“提高”了0.1~0.2s的速度

2.2xml布局优化

此部分适用于解析、处理、绘制静态xml时的优化

xml布局优化是老生常谈的话题了,本质是减少无谓的绘制,网上面试宝典很多,这里就也不介绍了。解决方法如下:

1.使用Include,Merge,viewStub简化布局
2.简单布局使用线性布局,复杂布局时使用相对布局,layer-list降低树的层级
3.使用gone标签可以跳过绘制
4.被遮挡的view避免重复绘制

3.延后启动耗时框架

本部分不能压缩总时间,只是将耗时操作移动到后面而已,可以让白屏时间减少0.2~0.3s(取决于框架数量)

3.1实现方法

在onCreate()的最后,加入post操作,即可实现在绘制xmlView完成后再进行非UI的耗时操作

getWindow().getDecorView().post(new Runnable()){
    @Override
    public void run()
        //加载Application中的框架 40+ms
        GlobalContext.startThirdFrameWork();
        //构建网络框架 120ms
        repo = SquareUtils.getRetrofit(URL).create(GithubService.class);
        //进行ssl库的初始化请求 40+ms
        onRefresh();
    }
});

3.2实现原理

在xml被inflate后,需要通过mDecoView.addView(xmlView)进行添加

addView最终调用ViewRootImpl的方法scheduleTraversals(),进行了消息队列的优先独占操作

mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

接着调用doTraversal()释放

mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

SyncBarrier拥有消息队列的独占性,当使用SyncBarrier时,后面的消息将被阻塞,这样在主线程中就有更多的CPU时间可以分给WMS进行绘图了。在View绘制完成后,解除SyncBarrier后才会调用我们在上文Post的耗时框架加载任务,这样就实现了延迟加载。

4. 多线程初始化

此部分真的可以压缩启动时间,但是对SDK线程安全有一定的要求,在黑盒SDK下容易出现问题

下文复用了OkHttp中的单例Worker线程池,节省了0.16s的启动时间

SquareUtils.getDispatcher().executorService().execute(new Runnable() {
    @Override 
    public void run() {
        Log.d(TAG, "run: " + System.currentTimeMillis());
        //42ms
        GlobalContext.startThirdFrameWork();
        //120ms
        repo = SquareUtils.getRetrofit(DanbooruAPI.KONACHAN).create(DanbooruAPI.class);
    runOnUiThread(new Runnable() {
     @Override 
         public void run() {
        //40ms
            onRefresh();
          }
        });
     }
});

5.混淆

经过测试,混淆在一定程度上可以提高速度,属于免费的性能提升,但是不是非常明显,大概只有100ms


混淆后要记得测试

6.总结

通过上述方法,可以压榨0.3~0.6s的时间,让用户能够更快的启动APP

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值