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請求與以後拼裝的時間加起來,比動態代理要多的多