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、一个是背景



        
    


    
     
    


设置windowBackground


在启动时先加载了伪背景,然后才加载了真正的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

你可能感兴趣的:(Android的冷启动优化)