隨着項目版本的迭代,App的性能問題會逐漸暴露出來,而好的用戶體驗與性能表現緊密相關,從本篇文章開始,我將開啓一個Android應用性能優化的專題,從理論到實戰,從入門到深挖,手把手將性能優化實踐到項目中,歡迎持續關注!html
那麼第一篇文章我就從應用的啓動優化開始,根據實際案例,打造閃電般的App啓動速度。android
來看一下Google官方文檔《Launch-Time Performance》對應用啓動優化的概述;git
應用的啓動分爲冷啓動、熱啓動、溫啓動,而啓動最慢、挑戰最大的就是冷啓動:系統和App自己都有更多的工做要從頭開始! 應用在冷啓動以前,要執行三個任務:github
而這三個任務執行完畢以後會立刻執行如下任務:shell
而一旦App進程完成了第一次繪製,系統進程就會用Main Activity替換已經展現的Background Window,此時用戶就可使用App了。數據庫
一樣,Google也給出了啓動加速的方向:緩存
- 利用提早展現出來的Window,快速展現出來一個界面,給用戶快速反饋的體驗;
備註:方向1屬於治標不治本,只是表面上快;方向二、3能夠真實的加快啓動速度。 接下來咱們就在項目中實際應用。性能優化
按照官方文檔的說明:使用Activity的windowBackground主題屬性來爲啓動的Activity提供一個簡單的drawable。 Layout XML file: 微信
這樣在啓動的時候,會先展現一個界面,這個界面就是Manifest中設置的Style,等Activity加載完畢後,再去加載Activity的界面,而在Activity的界面中,咱們將主題從新設置爲正常的主題,從而產生一種快的感受。不過如上文總結這種方式其實並無真正的加速啓動過程,而是經過交互體驗來優化了展現的效果。 備註:截圖一樣來自官方文檔《Launch-Time Performance》。markdown
經過代碼分析咱們能夠獲得App啓動的業務工做流程圖:
這一章節咱們重點關注初始化的部分:在Application以及首屏Activity中咱們主要作了:
項目中**除聽雲以外其他全部三方組件都搶佔先機,在Application主線程初始化。**這樣的初始化方式確定是太重的:
項目修改:
注意:閃屏頁的2秒停留能夠利用,把耗時操做延遲到這個時間間隔裏。
本節咱們實際定位耗時的操做,在開發階段咱們通常使用BlockCanary或者ANRWatchDog找耗時操做,簡單明瞭,可是沒法獲得每個方法的執行時間以及更詳細的對比信息。咱們能夠經過Method Tracing或者DDMS來得到更全面詳細的信息。 啓動應用,點擊 Start Method Tracing,應用啓動後再次點擊,會自動打開剛纔操做所記錄下的.trace文件,建議使用DDMS來查看,功能更加方便全面。
左側爲發生的具體線程,右側爲發生的時間軸,下面是發生的具體方法信息。注意兩列:Real Time/Call(實際發生時間),Calls+RecurCalls/Total(發生次數); 上圖咱們能夠獲得如下信息:
即使是耗時操做,可是隻要正確發生在WorkThread就沒問題。所以咱們**須要確認這些方法執行的線程以及發生的時機。這些操做若是發生在主線程,可能不構成ANR的發生條件,可是卡頓是再算不免的!**結合上章節圖App冷啓動業務工做流程圖中業務操做以及分析圖,再次查看代碼咱們能夠看到:部分耗時操做例如IO讀取等確實發生在主線程。事實上在traceview裏點擊執行函數的名稱不只能夠跟蹤到父類及子類的方法耗時,也能夠在方法執行時間軸中看到具體在哪一個線程以及耗時的界面閃動。
分析到部分耗時操做發生在主線程,那咱們把耗時操做都改到子線程是否是就萬事大吉了?非也!!
經過對traceview的詳細跟蹤以及代碼的詳細比對,我發現卡頓發生在:
以及其它細節問題:
項目修改: 1. 數據庫及IO操做都移到工做線程,而且設置線程優先級爲THREAD_PRIORITY_BACKGROUND,這樣工做線程最多能獲取到10%的時間片,優先保證主線程執行。
2. 流程梳理,延後執行; 實際上,這一步對項目啓動加速最有效果。經過流程梳理髮現部分流程調用時機偏早、失誤等,例如:
3.其它優化;
經過以上三步及三方組件的優化:Application以及首屏Activity回調期間主線程就沒有耗時、爭搶資源等狀況了。此外還涉及佈局優化、內存優化等部分技術,因對於應用冷啓動通常不是瓶頸點,這裏不展開詳談,可根據實際項目實際處理。
經過ADB命令統計應用的啓動時間:adb shell am start -W 首屏Activity。 同等條件下使用MX3及Nexus6P,啓動5次,比較優化前與優化後的啓動時間;
優化前: MX3
ThisTime | TotalTime | WaitTime |
---|---|---|
1237 | 2205 | 2214 |
1280 | 2181 | 2189 |
1622 | 2508 | 2513 |
1485 | 2434 | 2443 |
1442 | 2418 | 2429 |
Nexus6P
ThisTime | TotalTime | WaitTime |
---|---|---|
1229 | 1832 | 1868 |
1268 | 1849 | 1880 |
1184 | 1780 | 1812 |
1262 | 1845 | 1876 |
1164 | 1766 | 1807 |
優化後: MX3
ThisTime | TotalTime | WaitTime |
---|---|---|
865 | 1516 | 1523 |
911 | 1565 | 1573 |
812 | 1406 | 1418 |
962 | 1564 | 1574 |
925 | 1566 | 1577 |
Nexus6P
ThisTime | TotalTime | WaitTime |
---|---|---|
603 | 1192 | 1243 |
614 | 1076 | 1115 |
650 | 1120 | 1163 |
642 | 1107 | 1139 |
624 | 1084 | 1124 |
對比: MX3提高35%
ThisTime平均數 | TotalTime平均數 | WaitTime平均數 |
---|---|---|
優化前 | 1413 | 2349 |
優化後 | 895 | 1523 |
Nexus6P提高39%
ThisTime平均數 | TotalTime平均數 | WaitTime平均數 |
---|---|---|
優化前 | 1221 | 1814 |
優化後 | 626 | 1115 |
一、還能夠繼續優化的方向?
二、異步、延遲初始化及操做的依據? 注意一點:並非每個組件的初始化以及操做均可以異步或延遲;是否能夠取決組件的調用關係以及本身項目具體業務的須要。保證一個準則:能夠異步的都異步,不能夠異步的儘可能延遲。讓應用先啓動,再操做。
三、通用應用啓動加速套路?
四、其它
參考文章:《官方文檔——Launch-Time Performance》
歡迎關注微信公衆號:按期分享Java、Android乾貨!