Cold Startup Perfermance Improvement in Androidcss
本文依据平台以下html
在应用层,普通APP启动过程大体以下:java
为了提升用户感知,我但愿在主线程中执行的顺序以下(注意本流程不适用于插件化的App):android
不建议在Application中初始化耗时任务,它将直接致使白屏git
本部分能够提升上文1,2,3的用户体验github
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
最终可让用户以为“提升”了0.1~0.2s的速度
参考文章:
上述方案均不能很好处理状态栏,若是你使用Translucent,慎用
此部分适用于解析、处理、绘制静态xml时的优化
xml布局优化是老生常谈的话题了,本质是减小无谓的绘制,网上面试宝典不少,这里就也不介绍了。解决方法以下:
参考文章:
本部分不能压缩总时间,只是将耗时操做移动到后面而已,可让白屏时间减小0.2~0.3s(取决于框架数量)。
在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(); } });
在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的耗时框架加载任务,这样就实现了延迟加载。
此部分真的能够压缩启动时间,可是对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()--|
通过测试,混淆在必定程度上能够提升速度,属于免费的性能提高,可是不是很是明显,大概只有100ms
混淆后要记得测试
经过上述方法,能够压榨0.3~0.6s的时间,让用户可以更快的启动APP
本文例子: Github - AnimeWallpaper,目前启动速度0.7s,求各位star!
Retrofit 在知乎上有人这样回答的,大意是动态代理 == 反射 == 慢
,这就是典型的半桶水,不懂装懂。
经过对每一个方法进行统计后,结果倒是这样的:
retrofit构造(128ms)
retrofit访问网络前接口的拼装(42ms)
随着SSL的普及,javax.ssl必然会被加载,这个100ms的时间在native中黑盒执行,很难避免,只能等手机ROM去优化喽;剩下的就是Gson的时间比较久,这个时间仍是能够接受的。
从上面也能够看出,与动态代理相关的时间,并无想象中那么慢,不要看到反射就以为慢,网络I/O请求与以后拼装的时间加起来,比动态代理要多的多