冷启动优化-转摘https://www.jianshu.com/p/d97b390da87b

探究Android的冷启动优化

96 
BlackSwift 
2016.04.02 03:40* 字数 1464 阅读 4118评论 6

Cold Startup Perfermance Improvement in Androidcss


本文依据平台以下html

  • 机型: 魅蓝Note(高通615真八核/2G/1080P/4.4)
  • 效果:1.1s -> 0.7s(实际用户看到的假界面时间更短)
  • 检测网站: https://nimbledroid.com,是 @程序亦非猿 推荐的哦

1. 启动过程概述

在应用层,普通APP启动过程大体以下:java

  1. 加载Application
    • 静态代码段/构造函数
    • onCreate方法
  2. 加载主Activity
    • 静态代码段/构造函数
    • 消息队列第一次循环: onCreate,经过setContentview解析、加载xml
    • 消息队列第二次循环: 被动地调用Choreographerd中的FrameDisplayEventReceiver的run()进行进行实际绘制

为了提升用户感知,我但愿在主线程中执行的顺序以下(注意本流程不适用于插件化的App):android

  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中初始化耗时任务,它将直接致使白屏git

2. 用户感知优化

本部分能够提升上文1,2,3的用户体验github

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元素bash

 
伪背景加载 → 绘制完成View

最终可让用户以为“提升”了0.1~0.2s的速度

参考文章:

  1. avoding-android-cold-starts
  2. Android冷启动时间优化 - Wayne's blog
  3. GitHub - MaterialColdStart

上述方案均不能很好处理状态栏,若是你使用Translucent,慎用

2.2. XML布局优化

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

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

  1. 使用Include,Merge,viewStub简化布局
  2. 使用相对布局,layer-list下降树的层级
  3. 使用gone标签能够跳过绘制
  4. 被遮挡的view避免重复绘制

参考文章:

  1. http://stormzhang.com/android/2014/04/10/android-optimize-layout/
  2. http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0125/2356.html

3. 延后启动耗时框架

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

3.1. 实现方法

在onCreate()的最后,加入post操做,便可实如今绘制XmlView完成后再进行非UI的耗时操做

getWindow().getDecorView().post(new Runnable() { @Override public void run() { //加载Applicaiton中的框架 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(); } }); } }); 

最后,你就能比较充分利用你的真八核手机

主线程: 解析xml ----------addView()--------| → 更新界面
线程池: 初始化框架 --post(请求网络)---wait()--| 

5. 混淆

通过测试,混淆在必定程度上能够提升速度,属于免费的性能提高,可是不是很是明显,大概只有100ms

混淆后要记得测试

6. 总结

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

本文例子: Github - AnimeWallpaper,目前启动速度0.7s,求各位star!

附录. Retrofit框架加载时间分析

Retrofit 在知乎上有人这样回答的,大意是动态代理 == 反射 == 慢,这就是典型的半桶水,不懂装懂。

经过对每一个方法进行统计后,结果倒是这样的:

retrofit构造(128ms)

  • 构造OkHttp:121ms, 其中javax.ssl构建耗时117ms,调用的是一个SSL遍历native操做,这个基本没法避免;缓存文件初始化1ms
  • 构造GsonFactory 4ms: 主要是classloader加载的时间
  • 其余 3ms

retrofit访问网络前接口的拼装(42ms)

  • RxJava框架: 12ms
  • 动态代理: 1ms
  • Gson库: 27ms,主要进行反射操做
  • 其余: 2ms

随着SSL的普及,javax.ssl必然会被加载,这个100ms的时间在native中黑盒执行,很难避免,只能等手机ROM去优化喽;剩下的就是Gson的时间比较久,这个时间仍是能够接受的。

从上面也能够看出,与动态代理相关的时间,并无想象中那么慢,不要看到反射就以为慢,网络I/O请求与以后拼装的时间加起来,比动态代理要多的多

相关文章
相关标签/搜索